All Guides
gitgitversion-controlteamworkflow

Git Workflow for Modern Teams

Everything you need to know about effective Git workflows - branching strategies, commit conventions, PR best practices, and automation that keeps your repo clean at scale.

Ryan VerWey
2026-02-15
6 min read

The Foundation: Commit Discipline

Good Git habits compound over time. A well-maintained Git history is searchable documentation that explains why code exists, not just what it does.

Conventional Commits

Adopt Conventional Commits - a specification that makes commits machine-readable and human-scannable.

<type>(<scope>): <description>

[optional body]

[optional footer]

Types:

  • feat - New feature
  • fix - Bug fix
  • docs - Documentation changes
  • style - Formatting, no logic change
  • refactor - Code restructure, no behavior change
  • perf - Performance improvement
  • test - Adding or fixing tests
  • chore - Build process, dependency updates
  • ci - CI/CD changes

Examples:

git commit -m "feat(auth): add OAuth2 login with GitHub"
git commit -m "fix(api): handle null user in session middleware"
git commit -m "refactor(db): extract query helpers to separate module"
git commit -m "chore(deps): bump next from 15.1.0 to 15.2.0"

Writing the Body

For complex changes, the body explains why:

fix(payments): retry failed Stripe webhooks with exponential backoff

Stripe webhooks were failing silently when our database was temporarily 
unavailable during deployments. Added retry logic with exponential backoff 
(max 5 attempts, 30s max delay) and proper logging.

Closes #482

Branching Strategies

GitHub Flow (Recommended for Most Teams)

Simple, effective, and works well with CI/CD:

main                     (always deployable)
  ├── feature/user-auth  (short-lived feature branch)
  ├── fix/login-race     (short-lived bug fix)
  └── chore/bump-deps    (short-lived maintenance)

Rules:

  1. main is always deployable
  2. All work happens in feature branches
  3. Branches merge to main via PR
  4. Deploy from main automatically after merge

Gitflow (For Release-Oriented Projects)

More structure, more overhead - good for software with explicit versions:

main        (production releases, tagged)
develop     (integration branch)
  ├── feature/xxx   (feature branches off develop)
  └── release/1.2   (release prep branch)
hotfix/critical-fix  (branches off main)

Use Gitflow when:

  • You support multiple versions simultaneously
  • You have formal release cycles
  • You're shipping a library/SDK

Trunk-Based Development (Scale)

For large teams with strong CI:

main  (always shippable, deployed continuously)
  └── short-lived branches (hours, not days)

Feature flags control what's active in production. Requires excellent test coverage and CI.

Branch Naming Conventions

# Pattern: type/ticket-short-description
feature/JIRA-123-user-profile-page
fix/JIRA-456-null-pointer-checkout
chore/update-node-20
docs/api-authentication-guide

Pull Request Best Practices

PR Template

Create .github/pull_request_template.md:

## What does this PR do?
[Brief description of the changes]

## Why?
[Context and motivation]

## How to test
1. [Step-by-step testing instructions]

## Screenshots (if UI changes)

## Checklist
- [ ] Tests added/updated
- [ ] Documentation updated
- [ ] No secrets in code
- [ ] Ran linter/formatter

PR Size Guidelines

SizeLines ChangedExpectation
XS< 10Immediate review
S10-100Same day
M100-5001-2 days
L500+Consider splitting

Keep PRs small. Small PRs get better reviews, merge faster, and are easier to revert.

Draft PRs

Use draft PRs for work-in-progress. It signals "I want early feedback but this isn't ready to merge."

gh pr create --draft --title "WIP: user authentication"

Essential Git Commands

Daily Workflow

# Start fresh feature branch
git switch main && git pull
git switch -c feature/my-feature

# Stage changes granularly
git add -p  # Interactive staging (review every chunk)

# Commit
git commit -m "feat: add user authentication"

# Push and create PR
git push -u origin feature/my-feature
gh pr create

Keeping Up to Date

# Rebase your branch onto latest main (preferred)
git fetch origin
git rebase origin/main

# Or merge main into your branch
git merge origin/main

Fixing Mistakes

# Amend the last commit (before push)
git commit --amend --no-edit        # Add staged changes
git commit --amend -m "new message" # Change message

# Undo last local commit (keep changes staged)
git reset --soft HEAD~1

# Undo last local commit (discard changes)
git reset --hard HEAD~1

# Revert a pushed commit safely
git revert <commit-hash>

# Interactive rebase to clean up recent commits
git rebase -i HEAD~5

Useful Aliases

Add to your ~/.gitconfig:

[alias]
  s = status -s
  l = log --oneline --graph --decorate -20
  la = log --oneline --graph --decorate --all
  d = diff
  ds = diff --staged
  sw = switch
  co = checkout
  br = branch
  unstage = reset HEAD --
  last = log -1 HEAD
  undo = reset --soft HEAD~1

Git Hooks with Husky

Automate quality checks before commits:

npm install -D husky lint-staged
npx husky init

.husky/pre-commit:

#!/bin/sh
npx lint-staged

package.json:

{
  "lint-staged": {
    "*.{ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.{json,md,css}": ["prettier --write"]
  }
}

Commit Message Linting

npm install -D @commitlint/cli @commitlint/config-conventional

commitlint.config.js:

export default { extends: ['@commitlint/config-conventional'] };

.husky/commit-msg:

#!/bin/sh
npx --no -- commitlint --edit $1

Git Bisect (Finding Bugs)

One of the most underused Git features - binary search through commits to find what broke:

git bisect start
git bisect bad                  # current commit is broken
git bisect good v1.2.0          # this version was good

# Git checks out a mid-point commit
# Test your code, then:
git bisect good  # or: git bisect bad

# Repeat until git identifies the breaking commit
git bisect reset  # when done

Security: What NOT to Commit

Never commit:

  • API keys, tokens, passwords
  • .env files with real values
  • Private keys or certificates
  • Database connection strings with credentials

Preventive measures:

# Global gitignore
echo ".env" >> ~/.gitignore_global
git config --global core.excludesfile ~/.gitignore_global

# If you accidentally committed a secret:
git filter-repo --invert-paths --path path/to/secret
# Then invalidate the exposed credentials immediately!

Use git-secrets or gitleaks to scan for accidental secret commits.

The Golden Rules

  1. Commit early, commit often - small commits are easier to review and revert
  2. Never force-push to shared branches - coordinate or use --force-with-lease
  3. Delete merged branches - keep the repo tidy
  4. Pull before you push - avoid unnecessary merge commits
  5. Write the commit message you'd want to read - your future self will thank you
Ryan VerWey

Written by

Ryan VerWey

Ryan VerWey is a full-stack developer building tools and writing practical guides for working developers.