Ever had someone push broken code to production at 5pm on a Friday? Yeah, me too. Or watched two developers spend an entire morning untangling a merge conflict that should’ve been caught days earlier? Fun times.

Here’s the thing - most Git workflow guides are written for massive engineering teams. But if you’re running a small remote team (2-10 developers), you don’t need Facebook’s branching strategy. You need something that actually works without the overhead.

This is what we’ve learned after years of trial and error with small distributed teams. Real conventions that prevent disasters without drowning you in process.

⏱️ Reading time: 15-20 minutes

Why Bother With Git Strategy?

When I first started working with remote teams, we had zero structure. People pushed straight to main, merge conflicts were a weekly disaster, and nobody knew which branch was actually deployed to staging. Sound familiar?

Here’s what a simple Git workflow fixes:

  • No more Friday 5pm disasters - Clear rules mean fewer “oh crap” moments
  • Code actually gets reviewed - Branch protection makes it impossible to skip
  • Staging doesn’t break every other day - Separate environments are a lifesaver
  • New devs aren’t confused - They know exactly where to push their code on day one
  • Async teamwork works - Remote teams across timezones don’t step on each other
The Real Goal

A good Git strategy isn’t about following rules perfectly - it’s about making it harder to accidentally break production while keeping deployment fast.

We use a simplified Git Flow tailored for small teams, with four branch types:

%%{init: { 'theme': 'base', 'themeVariables': { 'git0': '#7B42BC', 'git1': '#2496ED', 'git2': '#00AA00', 'git3': '#FF9900', 'gitBranchLabel0': '#ffffff', 'gitBranchLabel1': '#ffffff', 'gitBranchLabel2': '#ffffff', 'gitBranchLabel3': '#ffffff', 'commitLabelColor': '#ffffff', 'commitLabelBackground': '#333333' }}}%%
gitGraph
  commit id: "Initial commit"
  branch develop
  checkout develop
  commit id: "Setup dev environment"

  branch feature/user-auth
  checkout feature/user-auth
  commit id: "Add login form"
  commit id: "Add authentication"
  checkout develop
  merge feature/user-auth
  commit id: "v1.1.0-dev" tag: "v1.1.0-dev"

  checkout main
  branch staging
  checkout staging
  merge develop tag: "v1.1.0-rc1"

  checkout main
  merge staging tag: "v1.1.0"

  checkout develop
  branch feature/dashboard
  checkout feature/dashboard
  commit id: "Create dashboard"
  checkout develop
  merge feature/dashboard

  checkout staging
  merge develop tag: "v1.2.0-rc1"

  checkout main
  merge staging tag: "v1.2.0"

How This Actually Works: Real Example

Let’s say Sarah needs to build user authentication, and Mike’s working on a dashboard. Two features, two developers, no drama. Here’s how it plays out.

Phase 1: Project Setup (Yes, boring but important)

# Someone (usually whoever starts the project) does this once:
git init
git commit -m "chore: initial commit"

# Create develop branch - this is where features get integrated
git checkout -b develop
git commit -m "chore: setup dev environment"
git push -u origin develop

What just happened? We’ve got a main branch (that’s production) and a develop branch (where stuff gets tested before going live). Simple.


Phase 2: Dev-A Works on User Authentication

Dev-A’s workflow:

# Dev-A creates feature branch from develop
git checkout develop
git pull origin develop
git checkout -b feature/user-auth

# Dev-A implements login form
git add src/auth/LoginForm.tsx
git commit -m "feat(auth): add login form component"

# Dev-A implements authentication logic
git add src/auth/authentication.ts
git commit -m "feat(auth): add JWT authentication logic"

# Dev-A pushes feature branch
git push -u origin feature/user-auth

What happened: Dev-A creates isolated commits on feature/user-auth without affecting others.


Phase 3: Dev-A Creates Pull Request

Dev-A creates PR:

# Dev-A ensures branch is up to date
git checkout feature/user-auth
git pull origin develop  # Get latest changes
git push origin feature/user-auth

# Create Pull Request via GitLab/GitHub UI or CLI
gh pr create \
  --base develop \
  --head feature/user-auth \
  --title "feat: Add user authentication system" \
  --body "## Changes
- Add login form component
- Implement JWT authentication
- Add token refresh mechanism

## Testing
- [x] Unit tests pass
- [x] Manual testing completed
- [x] No breaking changes

Closes #123"

PR Review Process:

  • Dev-B reviews the code
  • Dev-B leaves comments: “Please add error handling for token expiration”
  • Dev-A pushes fixes to the same branch
  • Dev-B approves the PR

Merge to develop:

# After PR approval, Dev-A or team lead merges
git checkout develop
git pull origin develop
git merge --no-ff feature/user-auth
git tag -a v1.1.0-dev -m "Dev release: User authentication"
git push origin develop --follow-tags

# Clean up feature branch
git branch -d feature/user-auth
git push origin --delete feature/user-auth

What happened:

  • PR ensured code review before merge
  • User authentication integrated in develop and tagged as v1.1.0-dev
  • Feature branch deleted (clean repository)

Phase 4: Deploy to Staging for Testing

Team lead creates PR: develop → staging:

# Team lead creates staging release PR
gh pr create \
  --base staging \
  --head develop \
  --title "release: v1.1.0-rc1 - User authentication" \
  --body "## Release Candidate v1.1.0-rc1

### Features
- User authentication system (PR #45)

### QA Checklist
- [ ] Login flow works correctly
- [ ] Token refresh working
- [ ] Error handling verified
- [ ] Performance acceptable
- [ ] Security scan passed

### Deployment
- Target: Staging environment
- Expected deployment time: 5 minutes"

Merge to staging:

# After PR approval from team lead
git checkout staging
git pull origin staging
git merge --no-ff develop
git tag -a v1.1.0-rc1 -m "Release Candidate: User authentication"
git push origin staging --follow-tags

What happened:

  • PR documents what’s being released to staging
  • Staging branch created from main (mirrors production)
  • develop merged to staging with RC (Release Candidate) tag
  • QA team tests on staging environment
  • CI/CD automatically deploys to staging server

Phase 5: Deploy to Production

After successful testing on staging, team lead creates production PR:

# Team lead creates production release PR
gh pr create \
  --base main \
  --head staging \
  --title "release: v1.1.0 - User authentication system" \
  --body "## Production Release v1.1.0

### What's New
- User authentication with JWT tokens
- Login/logout functionality
- Token refresh mechanism

### QA Status
- All tests passed on staging
- Manual QA completed
- Security scan: No issues
- Performance: Within acceptable limits

### Rollback Plan
If issues occur, rollback to v1.0.0:
\`\`\`bash
git checkout main
git reset --hard v1.0.0
git push --force origin main
\`\`\`"

Merge to production:

# After all approvals (requires 2 reviewers for main)
git checkout main
git merge --no-ff staging
git tag -a v1.1.0 -m "Release v1.1.0: User authentication system"
git push origin main --follow-tags

What happened:

  • PR requires 2 approvals (protected branch rule)
  • All stakeholders signed off
  • User authentication is now live in production with version v1.1.0
  • Rollback plan documented in PR

Phase 6: Dev-B Works on Dashboard (Parallel Development)

Meanwhile, Dev-B starts working (even before v1.1.0 goes to production):

# Dev-B creates feature branch from latest develop
git checkout develop
git pull origin develop  # Gets v1.1.0-dev code
git checkout -b feature/dashboard

# Dev-B implements dashboard
git add src/dashboard/
git commit -m "feat(dashboard): create user dashboard with metrics"

# Dev-B pushes work
git push -u origin feature/dashboard

What happened: Dev-B works independently on feature/dashboard while Dev-A’s code is being tested.


Phase 7: Dev-B Creates Pull Request for Dashboard

Dev-B creates PR:

# Dev-B ensures feature is ready
git checkout feature/dashboard
git pull origin develop  # Sync with latest develop
git push origin feature/dashboard

# Create Pull Request
gh pr create \
  --base develop \
  --head feature/dashboard \
  --title "feat: Add user dashboard with metrics" \
  --body "## Changes
- Create dashboard layout component
- Add real-time metrics display
- Implement data fetching hooks

## Screenshots
![Dashboard preview](./screenshots/dashboard.png)

## Testing
- [x] Unit tests pass
- [x] Integration tests pass
- [x] Tested on Chrome, Firefox, Safari

## Dependencies
- Requires authentication feature (already merged)

Closes #124"

PR Review Process:

  • Dev-A reviews the code
  • Dev-A comments: “Great work! Just one suggestion: add loading states”
  • Dev-B pushes update
  • Dev-A approves

Merge to develop:

# After PR approval
git checkout develop
git pull origin develop
git merge --no-ff feature/dashboard
# No tag needed this time (optional for dev releases)
git push origin develop

# Clean up
git branch -d feature/dashboard
git push origin --delete feature/dashboard

What happened:

  • Dev-A and Dev-B collaborated via PR review
  • Dashboard feature integrated into develop
  • Code quality maintained through peer review

Phase 8: Deploy Dashboard to Staging

Team lead creates staging PR:

# Create PR: develop → staging
gh pr create \
  --base staging \
  --head develop \
  --title "release: v1.2.0-rc1 - User dashboard" \
  --body "## Release Candidate v1.2.0-rc1

### New Features
- User dashboard with real-time metrics (PR #46)

### Included in this release
- User authentication (v1.1.0)
- Dashboard feature (v1.2.0)

### QA Checklist
- [ ] Dashboard displays correctly
- [ ] Metrics update in real-time
- [ ] Loading states work
- [ ] Compatible with existing auth

### Deployment
- Target: Staging environment"

Merge to staging:

# After PR approval
git checkout staging
git pull origin staging
git merge --no-ff develop
git tag -a v1.2.0-rc1 -m "Release Candidate: Dashboard feature"
git push origin staging --follow-tags

What happened:

  • PR documents the dashboard release
  • Staging now has both user auth (v1.1.0) and dashboard features for testing
  • QA team validates on staging

Phase 9: Deploy Dashboard to Production

After QA approval, team lead creates production PR:

# Create production release PR
gh pr create \
  --base main \
  --head staging \
  --title "release: v1.2.0 - User dashboard" \
  --body "## Production Release v1.2.0

### What's New
- User dashboard with real-time metrics
- Performance monitoring charts
- Responsive design for mobile/desktop

### QA Status
- All staging tests passed
- Performance: Page load < 2s
- Accessibility: WCAG AA compliant
- Cross-browser tested

### Rollback Plan
If issues occur, rollback to v1.1.0:
\`\`\`bash
git checkout main
git reset --hard v1.1.0
git push --force origin main
\`\`\`

### Deployment Window
- Scheduled: Tuesday 2PM UTC (low traffic period)
- Duration: ~5 minutes
- Monitoring: 1 hour post-deployment"

Merge to production:

# After 2 reviewers approve
git checkout main
git merge --no-ff staging
git tag -a v1.2.0 -m "Release v1.2.0: User dashboard"
git push origin main --follow-tags

What happened:

  • Production PR required 2 approvals
  • All stakeholders reviewed and signed off
  • Dashboard is now live in production as version v1.2.0
  • Team monitors deployment for 1 hour

Key Takeaways from This Workflow

Parallel Development

  • Dev-A and Dev-B worked simultaneously without conflicts
  • Feature branches kept work isolated

Quality Gates

  • Every feature went through: develop → staging → main
  • QA tested on staging before production

Clear History

  • Version tags show what’s in each release
  • Easy to track what went to production and when

Safe Rollbacks

  • If v1.2.0 breaks, rollback to v1.1.0 using git tags
  • Staging environment caught issues before production

Team Coordination

  • Develop branch integrates all features
  • Staging acts as pre-production testing ground
  • Main always represents production state

Branch Structure Overview

BranchPurposeLifetimeProtectedDeploys To
mainProduction-ready codePermanent✅ YesProduction
stagingPre-production testingPermanent✅ YesStaging Environment
developIntegration branchPermanent✅ YesDevelopment Environment
feature/*Feature developmentTemporary❌ NoPreview (optional)
bugfix/*Bug fixesTemporary❌ No-
hotfix/*Emergency production fixesTemporary❌ No-

Branch Purposes and Rules

1. Main Branch (main)

Purpose: Contains only production-ready, tested, and approved code.

Rules:

  • Always deployable to production
  • Protected - no direct commits allowed
  • Only accepts merges from staging branch
  • Requires code review approval
  • All CI/CD tests must pass
  • Tagged with semantic versions (v1.0.0, v1.1.0, etc.)

Who can merge: Team lead or designated release manager

Deployment: Automatically deploys to production after merge (with approval)

# ❌ NEVER do this
git checkout main
git commit -m "Quick fix"
git push

# ALWAYS do this
git checkout -b hotfix/critical-bug
# Make changes
git push origin hotfix/critical-bug
# Create pull request to staging → main

2. Staging Branch (staging)

Purpose: Pre-production testing environment that mirrors production.

Rules:

  • Protected - no direct commits allowed
  • Only accepts merges from develop or hotfix/* branches
  • Requires 1 code review approval
  • All automated tests must pass
  • Used for QA and client demos
  • Tagged with release candidates (v1.1.0-rc1, v1.1.0-rc2)

Who can merge: Any developer with approval

Deployment: Automatically deploys to staging environment

Testing checklist before promoting to main:

  • All features work as expected
  • No breaking changes
  • Performance is acceptable
  • Security scan passed
  • Client/stakeholder approval (if needed)

3. Develop Branch (develop)

Purpose: Main integration branch where features come together.

Rules:

  • Protected - no direct commits allowed
  • Accepts merges from feature/* and bugfix/* branches
  • Requires code review (can be less strict than staging)
  • CI/CD tests should pass
  • Most active branch for development

Who can merge: Any developer after review

Deployment: Automatically deploys to development environment

# Feature is complete and tested
git checkout develop
git pull origin develop
git merge --no-ff feature/user-authentication
git push origin develop

4. Feature Branches (feature/*)

Purpose: Develop new features in isolation.

Rules:

  • Branch from develop
  • Merge back to develop when complete
  • Delete after merging
  • Keep focused on single feature
  • Regularly sync with develop to avoid conflicts

Lifetime: Created when starting feature → Deleted after merge

Naming convention: feature/short-descriptive-name

# Create feature branch
git checkout develop
git pull origin develop
git checkout -b feature/user-dashboard

# Work on feature
git add .
git commit -m "feat: add user dashboard layout"
git push origin feature/user-dashboard

# Keep up to date with develop
git checkout develop
git pull origin develop
git checkout feature/user-dashboard
git merge develop

5. Bugfix Branches (bugfix/*)

Purpose: Fix bugs found in development or staging.

Rules:

  • Branch from develop
  • Merge back to develop
  • Delete after merging

Naming convention: bugfix/issue-description

6. Hotfix Branches (hotfix/*)

Purpose: Emergency fixes for production issues.

Rules:

  • Branch from main
  • Merge to both main AND develop
  • Highest priority
  • Minimal changes only

Naming convention: hotfix/critical-issue-description

Workflow:

# Production is broken!
git checkout main
git pull origin main
git checkout -b hotfix/payment-gateway-crash

# Fix the issue
git add .
git commit -m "fix: resolve payment gateway timeout"
git push origin hotfix/payment-gateway-crash

# Create pull requests:
# 1. hotfix/payment-gateway-crash → staging → main
# 2. hotfix/payment-gateway-crash → develop

Branch Protection Rules

Here’s what I actually configure in GitHub/GitLab - not every checkbox, just the ones that prevent disasters:

Protect Your Main Branch (Non-negotiable)

This is production. Lock it down.

  • 2 approvals required - No solo cowboys merging to prod
  • CI must pass - Build, tests, and security scans
  • No force pushes - Seriously, just don’t
  • 🔒 Only team leads can merge - Someone needs to be responsible

What I skip: Signed commits (nice in theory, annoying in practice for small teams), conversation resolution (we’re adults), branch deletion protection (not needed if you’re not reckless).

Accidentally Committed to Main?

If you forgot to create a feature branch and committed directly to main (we’ve all done it), check out our guide on how to fix accidental commits to main. It walks you through moving your commits to a feature branch safely without losing any work.

Staging Branch Protection

Lighter than main, but still protected:

  • 1 approval required - Quick review before testing
  • CI must pass - Build and tests (skip the heavy scans)
  • No force pushes - Keep history clean

Develop Branch Protection

Basic guardrails:

  • 1 approval - Catch obvious issues
  • Build + lint must pass - Don’t break the integration branch
  • No force pushes - Play nice with others

You can add more protections later, but start with these and you’ll prevent 90% of problems.

Naming Conventions

Branch Naming

Follow this pattern: <type>/<short-description>

Types:

  • feature/ - New features
  • bugfix/ - Bug fixes
  • hotfix/ - Production hotfixes
  • refactor/ - Code refactoring
  • docs/ - Documentation updates
  • test/ - Adding tests
  • chore/ - Maintenance tasks

Examples:

✅ Good Branch Names❌ Bad Branch NamesWhy?
feature/user-authenticationfix-stuffVague, no context on what’s being fixed
feature/payment-integrationjohns-branchWho’s John? What’s he working on?
bugfix/login-redirect-looptemp”Temporary” branches that live forever
hotfix/database-connection-timeoutnew-featureWhich feature? There are 20 features
refactor/api-error-handlingbranch-1Numbers mean nothing 2 weeks later
docs/api-documentationwipEverything is “work in progress”
feature/123-user-dashboardtestTesting what? Too generic

Branch naming rules (the non-negotiables):

  • Kebab-case only - feature/user-auth, not feature/userAuth or feature/user_auth
  • 2-4 words max - Be descriptive but concise (feature/add-user-profile-page is pushing it)
  • Present tense - feature/add-login not feature/added-login or feature/adding-login
  • Include issue numbers - feature/123-user-dashboard links to issue #123
  • No special characters - Only / and - allowed (no underscores, spaces, or weird symbols)

Commit Messages (Keep It Simple)

You just follow this basic pattern:

<type>: what you actually did

The types I use 90% of the time:

  • feat: - New feature (e.g., “feat: add login page”)
  • fix: - Bug fix (e.g., “fix: prevent crash on empty input”)
  • chore: - Boring maintenance (e.g., “chore: update dependencies”)

That’s it. Yes, there are like 10 more types (docs, style, refactor, perf, test, ci, build…), but honestly? Start with these three. You can get fancy later.

Good commit messages:

feat: add JWT token refresh mechanism
fix: resolve timeout in user API endpoint
chore: update axios to v1.6.0

Bad commit messages (we’ve all done these):

fixed bug
updated files
WIP
asdfgh
more stuff
final version
final version 2  # <- guilty

See the difference? One tells you nothing, the other tells you exactly what changed.

Optional scope - If your project is big enough:

feat(auth): add JWT refresh
fix(api): resolve user endpoint timeout

But for small teams? The simple version works fine.

Commit message template:

Create .gitmessage in your repository:

# <type>(<scope>): <subject>
# |<----  Using a Maximum Of 50 Characters  ---->|

# Explain why this change is being made
# |<----   Try To Limit Each Line to a Maximum Of 72 Characters   ---->|

# Provide links or keys to any relevant tickets, articles or other resources
# Example: Closes #23

# --- COMMIT END ---
# Type can be
#    feat     (new feature)
#    fix      (bug fix)
#    refactor (refactoring production code)
#    style    (formatting, missing semi colons, etc; no code change)
#    docs     (changes to documentation)
#    test     (adding or refactoring tests; no production code change)
#    chore    (updating grunt tasks etc; no production code change)
#    perf     (performance improvements)
#    ci       (CI/CD changes)
#    build    (build system changes)
# --------------------
# Remember to
#    Capitalize the subject line
#    Use the imperative mood in the subject line
#    Do not end the subject line with a period
#    Separate subject from body with a blank line
#    Use the body to explain what and why vs. how
#    Can use multiple lines with "-" for bullet points in body
# --------------------

Configure git to use this template:

git config --global commit.template .gitmessage

Tag Naming Convention

Use Semantic Versioning (semver.org): vMAJOR.MINOR.PATCH

Format: vX.Y.Z

  • MAJOR (X): Breaking changes
  • MINOR (Y): New features (backward compatible)
  • PATCH (Z): Bug fixes (backward compatible)

Examples:

# Production releases (on main)
v1.0.0    # Initial release
v1.1.0    # Added new feature
v1.1.1    # Bug fix
v2.0.0    # Breaking changes

# Release candidates (on staging)
v1.1.0-rc1    # First release candidate
v1.1.0-rc2    # Second release candidate

# Pre-releases (on develop)
v1.1.0-alpha
v1.1.0-beta

Creating tags:

# Annotated tag (recommended)
git tag -a v1.2.0 -m "Release version 1.2.0: Add user dashboard and notifications"

# Push tag to remote
git push origin v1.2.0

# Push all tags
git push origin --tags

Complete Workflow Examples

Scenario 1: Developing a New Feature

# 1. Start from latest develop
git checkout develop
git pull origin develop

# 2. Create feature branch
git checkout -b feature/notification-system

# 3. Work on feature (make multiple commits)
git add src/notifications/
git commit -m "feat(notifications): add email notification service"

git add src/notifications/
git commit -m "feat(notifications): add push notification support"

git add tests/notifications/
git commit -m "test(notifications): add notification service tests"

# 4. Keep branch updated (if develop has moved ahead)
git checkout develop
git pull origin develop
git checkout feature/notification-system
git merge develop

# 5. Push feature branch
git push origin feature/notification-system

# 6. Create Pull Request: feature/notification-system → develop
# - Add description of changes
# - Request reviewer
# - Link related issues

# 7. After approval and merge, delete branch
git checkout develop
git pull origin develop
git branch -d feature/notification-system
git push origin --delete feature/notification-system

Scenario 2: Releasing to Production

# 1. Merge develop to staging
git checkout staging
git pull origin staging
git merge develop
git push origin staging

# Tag release candidate
git tag -a v1.3.0-rc1 -m "Release candidate for v1.3.0"
git push origin v1.3.0-rc1

# 2. Test on staging environment
# - Run manual tests
# - Check all features
# - Get stakeholder approval

# 3. If tests pass, merge staging to main
git checkout main
git pull origin main
git merge staging
git push origin main

# 4. Tag production release
git tag -a v1.3.0 -m "Release v1.3.0: Add notification system and user dashboard"
git push origin v1.3.0

# 5. Ensure develop is synced
git checkout develop
git merge main
git push origin develop

Scenario 3: Emergency Production Hotfix

# 1. Branch from main
git checkout main
git pull origin main
git checkout -b hotfix/payment-api-timeout

# 2. Fix the issue
git add src/payment/
git commit -m "fix(payment): increase API timeout to 30s"

# 3. Test locally
npm test

# 4. Push and create PR to staging first
git push origin hotfix/payment-api-timeout
# Create PR: hotfix/payment-api-timeout → staging

# 5. After staging tests pass, merge to main
# Create PR: staging → main

# 6. Merge hotfix to develop
git checkout develop
git pull origin develop
git merge hotfix/payment-api-timeout
git push origin develop

# 7. Tag hotfix release
git checkout main
git tag -a v1.3.1 -m "Hotfix v1.3.1: Fix payment API timeout"
git push origin v1.3.1

# 8. Delete hotfix branch
git branch -d hotfix/payment-api-timeout
git push origin --delete hotfix/payment-api-timeout

Scenario 4: Handling Merge Conflicts

# You're on feature branch and want to merge develop
git checkout feature/user-profile
git merge develop

# Conflict detected!
# Auto-merging src/components/Profile.tsx
# CONFLICT (content): Merge conflict in src/components/Profile.tsx

# 1. Check conflicted files
git status

# 2. Open files and resolve conflicts
# Look for conflict markers:
# <<<<<<< HEAD
# Your changes
# =======
# Changes from develop
# >>>>>>> develop

# 3. After resolving, mark as resolved
git add src/components/Profile.tsx

# 4. Complete the merge
git commit -m "merge: resolve conflicts with develop"

# 5. Push updated branch
git push origin feature/user-profile

Pull Request Guidelines

Creating Pull Requests

PR Title Format:

Your PR title is what shows up in changelogs, Git history, and Slack notifications. Make it count.

Format:

<type>: <what you actually did>

Good PR titles (clear, actionable, tells you what changed):

feat: Add user notification system
fix: Resolve login redirect loop on expired tokens
docs: Update API authentication guide
refactor: Optimize database query performance in user service
hotfix: Patch XSS vulnerability in comment form
feat(auth): Add JWT refresh token rotation

Bad PR titles (vague, doesn’t tell you anything useful):

Update notifications          # Update how? What changed?
Fixed bug                     # Which bug? Where?
Docs                          # What docs? Too vague
Code cleanup                  # What code? Cleanup how?
Security fix                  # Need specifics for hotfixes
Add feature                   # Which feature?

Title length: Aim for 50-72 characters. If you can’t fit it, your PR is probably too big.

PR Description Template:

## Description

Brief description of changes made

## Type of Change

- [ ] New feature
- [ ] Bug fix
- [ ] Breaking change
- [ ] Documentation update
- [ ] Refactoring
- [ ] Performance improvement

## Related Issues

Closes #123
Related to #456

## Changes Made

- Added notification email service
- Implemented push notification support
- Created notification preferences UI
- Added unit tests for notification service

## Testing

- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] Manual testing completed
- [ ] Tested on Chrome, Firefox, Safari

## Screenshots (if applicable)

[Add screenshots here]

## Checklist

- [ ] Code follows project style guidelines
- [ ] Self-review completed
- [ ] Comments added for complex code
- [ ] Documentation updated
- [ ] No new warnings introduced
- [ ] Tests added/updated
- [ ] All tests passing

Code Review Checklist

For Reviewers:

  • Code follows team conventions
  • No unnecessary complexity
  • Proper error handling
  • Security considerations addressed
  • Performance implications considered
  • Tests are adequate
  • Documentation is updated
  • No debugging code left behind
  • Commit messages follow convention

Merging Strategies

We use different strategies for different branches:

# For feature → develop: Squash and merge (clean history)
git checkout develop
git merge --squash feature/user-auth
git commit -m "feat: add user authentication system"

# For develop → staging: Merge commit (preserve feature commits)
git checkout staging
git merge --no-ff develop

# For staging → main: Merge commit (preserve all history)
git checkout main
git merge --no-ff staging

Why different strategies?

  • Squash merge: Keeps develop history clean, one commit per feature
  • Merge commit: Preserves detailed history for staging and production

Automating Quality Checks with Git Hooks

Git hooks are your first line of defense against bad code reaching your repository. They automatically validate code quality, enforce commit standards, and catch mistakes before they embarrass you in code review or break CI.

Automating Quality Checks with Git Hooks

Git hooks are your first line of defense against bad code reaching your repository. They automatically validate code quality, enforce commit standards, and catch mistakes before they embarrass you in code review or break CI.

What Git hooks can prevent:

  • Linting errors and failing tests
  • Invalid commit message formats
  • Hardcoded secrets or API keys
  • Direct commits to protected branches

Instead of repeating the full setup here, we’ve created a dedicated guide that covers everything you need to know about Git hooks - from basic pre-commit checks to advanced patterns like auto-formatting and secret detection.

Complete Git Hooks Guide

For detailed instructions on setting up Git hooks with Husky, integrating with CI/CD, and common hook patterns, see our dedicated guides:

Git Hooks for Automation: Catch Mistakes Before They Embarrass You → - Theory and patterns

Implementing Git Hooks with Husky: A Real-World Example → - Practical setup guide

The guides include:

  • Pre-commit, commit-msg, and pre-push hook examples
  • Husky setup for team-wide hook sharing
  • Real-world implementation in an Astro project
  • Common patterns (prevent commits to main, check for secrets)
  • Troubleshooting and CI/CD integration

Setting up hooks takes 5 minutes and saves hours of debugging. Highly recommended for any team workflow.

For complete CI/CD integration examples (including multi-environment deployments), see the Git Hooks automation guide.

Team Workflow Best Practices

Daily Development Flow

Morning:

# Start your day by syncing
git checkout develop
git pull origin develop

During Development:

# Commit often with good messages
git add .
git commit -m "feat(feature): add specific functionality"

# Push regularly to backup and share progress
git push origin feature/your-feature

Before Lunch/End of Day:

# Sync with develop to avoid conflicts later
git checkout develop
git pull origin develop
git checkout feature/your-feature
git merge develop

Communication Guidelines

When to notify team:

  • Before merging to develop (affects everyone)
  • Before merging to staging (for QA/testing)
  • Before merging to main (production release)
  • When deploying hotfixes
  • When encountering blocking issues

Communication channels:

  • Slack/Teams: Quick updates, PR reviews needed
  • Daily standups: Current branch, blockers
  • PR comments: Code-specific discussions
  • Documentation: Architectural decisions

Troubleshooting Common Issues

Issue 1: “I committed to wrong branch”

# You're on develop but should be on feature branch

# 1. Create feature branch from current state
git checkout -b feature/my-feature

# 2. Reset develop to remote state
git checkout develop
git reset --hard origin/develop

# 3. Your commits are now on feature branch
git checkout feature/my-feature

Issue 2: “I need to undo last commit”

# Undo commit but keep changes
git reset --soft HEAD~1

# Undo commit and discard changes
git reset --hard HEAD~1

# Undo commit that was already pushed (creates new commit)
git revert HEAD

Issue 3: “My branch is behind main”

# Update your branch with latest main
git checkout feature/my-feature
git fetch origin
git merge origin/main

# Or use rebase for cleaner history
git rebase origin/main

Issue 4: “Accidentally pushed to protected branch”

# If push was rejected - good! Branch protection worked
# If push succeeded - contact admin immediately

# Admin can:
# 1. Revert the commit
git revert <commit-hash>
git push origin main

# 2. Or reset branch (if no one pulled yet)
git reset --hard <commit-before-mistake>
git push --force origin main  # Only admin can do this

Metrics and Monitoring

Track these metrics to improve your workflow:

Git Health Metrics

  • Average PR time to merge: < 24 hours
  • Branch lifetime: Feature branches < 3 days
  • Merge conflicts: < 10% of merges
  • Failed builds: < 5% of pushes
  • Hotfix frequency: < 1 per week

Tools for Monitoring

# Check branch age
git for-each-ref --sort=committerdate refs/heads/ \
  --format='%(committerdate:short) %(refname:short)'

# Count commits per author
git shortlog -sn --all

# Check merge frequency
git log --merges --oneline | wc -l

Onboarding New Team Members

Day 1 Checklist

  • Clone repository
  • Install git hooks
  • Read this guide
  • Configure git username and email
  • Set up commit message template
  • Add SSH key to Git host
  • Join team chat channels
  • Review recent PRs

Practice Exercise

Give new members a safe task:

# 1. Create practice branch
git checkout -b feature/onboarding-<name>

# 2. Make a small change
# Edit README.md - add your name to contributors

# 3. Commit with proper format
git commit -m "docs(readme): add <name> to contributors"

# 4. Push and create PR
git push origin feature/onboarding-<name>

# 5. Get it reviewed and merged

Summary Cheat Sheet

Quick Commands

# Start new feature
git checkout develop && git pull && git checkout -b feature/name

# Update feature with develop
git checkout develop && git pull && git checkout - && git merge develop

# Finish feature
git push origin feature/name  # Then create PR

# Quick status
git status && git log --oneline -5

# Clean up merged branches
git branch --merged | grep -v "\*" | xargs -n 1 git branch -d

Branch Lifecycle

Here’s how branches flow through your environments from creation to deletion:

flowchart TD
  A[Create Feature Branch] --> B[Develop & Commit]
  B --> C[Open PR to develop]
  C --> D{Code Review}
  D -->|Changes Requested| B
  D -->|Approved| E[Merge to develop]
  E --> F[Auto-deploy to DEV env]
  F --> G[Test in DEV]
  G --> H{Ready for Staging?}
  H -->|Not Yet| B
  H -->|Yes| I[Merge to staging]
  I --> J[Auto-deploy to STAGING]
  J --> K[QA Testing]
  K --> L{Production Ready?}
  L -->|Issues Found| B
  L -->|Approved| M[Merge to main]
  M --> N[Deploy to PRODUCTION]
  N --> O[Delete Feature Branch]

  style A fill:#e1f5ff,stroke:#0066cc,stroke-width:2px
  style E fill:#fff4e1,stroke:#ff9900,stroke-width:2px
  style I fill:#ffeaa7,stroke:#fdcb6e,stroke-width:2px
  style M fill:#d4f4dd,stroke:#00b894,stroke-width:2px
  style O fill:#ff7979,stroke:#d63031,stroke-width:2px
  style D fill:#ffd700,stroke:#ff8800,stroke-width:2px
  style H fill:#ffd700,stroke:#ff8800,stroke-width:2px
  style L fill:#ffd700,stroke:#ff8800,stroke-width:2px

Branch lifespan by type:

Branch TypeCreated WhenDeleted WhenTypical Lifespan
feature/*Starting new workAfter merged to develop1-7 days
bugfix/*Fixing non-critical bugAfter merged to develop1-3 days
hotfix/*Production emergencyAfter merged to main2-4 hours
developProject startNever (permanent)Entire project
stagingProject startNever (permanent)Entire project
mainProject startNever (permanent)Entire project

Key points:

  • Feature branches are temporary - delete them after merging
  • Long-lived feature branches (>1 week) = code review hell
  • If your feature branch is older than 2 weeks, you’re doing something wrong (break it down!)

Protection Summary

BranchProtectionRequired ApprovalsAuto-Deploy
mainMaximum2 reviewersManual only
stagingHigh1 reviewerAuto to staging
developMedium1 reviewerAuto to dev
feature/*None-Optional preview

The Bottom Line

Look, you made it through 1500+ lines of Git workflow documentation. Here’s what actually matters:

This workflow gives you:

  • No more Friday 5pm disasters - Branch protection catches mistakes before they hit prod
  • Code reviews that actually happen - Not optional when it’s required to merge
  • New developers who aren’t lost - They know exactly where to push on day one
  • Confidence when deploying - Staging caught the bugs, production is clean

Your team doesn’t need to follow this exactly. Got a team of 3? Skip some of the ceremony. Growing to 10? Add more guardrails. The point isn’t perfection - it’s having some structure so nobody accidentally nukes production.

Start with protecting your main branch this week. Add the rest as you go. Your future self (and your team) will thank you when you’re NOT debugging production at midnight.

Next Steps: Implement This Today

Ready to implement this strategy? Here’s your 3-week action plan:

Week 1: Foundation

Day 1 - Set Up Branch Protection (30 minutes)

  • Protect main branch with 2 required approvals
  • Protect staging and develop with 1 required approval
  • Enable status checks (CI/CD must pass)

Day 2 - Create Documentation (1 hour)

  • Add GIT_WORKFLOW.md to your repository (use template below)
  • Share this guide with your team
  • Schedule a 30-minute team walkthrough

Day 3 - Install Git Hooks (1 hour)

  • Set up Husky and commitlint
  • Configure pre-commit linting checks
  • Test hooks with a dummy commit
Week 2: Practice
  • Each team member practices with a feature branch
  • Create at least one PR per person
  • Review and refine the workflow based on feedback
  • Address questions and concerns in team standup
Week 3: Full Adoption
  • Move all active work to the new workflow
  • Monitor merge conflicts (should decrease)
  • Measure PR review times
  • Celebrate wins and learn from challenges

Have questions about implementing this workflow? Check out our related guides or reach out in the comments below.

Additional Resources

Appendix: Team Agreement Template

Create a GIT_WORKFLOW.md file in your repository:

# Our Git Workflow Agreement

## Branches We Use

- main (production)
- staging (pre-production)
- develop (integration)
- feature/\* (features)
- hotfix/\* (emergency fixes)

## Rules We Follow

1. Never commit directly to main, staging, or develop
2. Always create PR for code review
3. Delete branches after merging
4. Write meaningful commit messages
5. Keep feature branches short-lived (<3 days)
6. Sync with develop daily
7. Test before pushing
8. Tag all production releases

## Code Review Standards

- Response time: < 24 hours
- Be constructive and specific
- Approve only if you'd deploy it
- Block if security/performance issues

## Emergency Protocol

1. Hotfix branches from main
2. Notify team in #urgent channel
3. Fast-track review
4. Deploy immediately after testing
5. Create incident report

Signed by team: [Names and dates]

Need help implementing this workflow? Start small - protect your main branch first, then gradually add the other protections and conventions as your team grows comfortable with the process.