Skip to main content
Back to Blog
GitVersion ControlTeam CollaborationDevOpsBest PracticesGitHub

Git Workflow Best Practices for Teams: A Complete Guide

Master Git workflows for team collaboration. Learn branching strategies, commit conventions, code review processes, and conflict resolution techniques.

6 min read

Git is straightforward for solo projects but gets complex with teams. After working on codebases with teams ranging from 3 to 50+ developers, I've learned which workflows scale and which create chaos. Here's what actually works in production environments.

Choosing a Branching Strategy

GitHub Flow (Simple and Effective)

Best for: Teams with continuous deployment, smaller projects

main (always deployable)
  |
  └── feature/user-authentication
        |
        └── PR → Code Review → Merge → Deploy
# Create feature branch from main
git checkout main
git pull origin main
git checkout -b feature/add-payment-integration

# Work on feature
git add .
git commit -m "feat: add Stripe payment integration"

# Push and create PR
git push -u origin feature/add-payment-integration

GitFlow (Structured Releases)

Best for: Teams with scheduled releases, enterprise software

main (production)
  |
develop (integration)
  |
  ├── feature/new-dashboard
  ├── feature/user-settings
  |
release/v2.0
  |
hotfix/critical-bug
# Start a new feature
git checkout develop
git pull origin develop
git checkout -b feature/new-dashboard

# Complete feature
git checkout develop
git merge feature/new-dashboard

# Create release
git checkout -b release/v2.0 develop

# Hotfix production
git checkout main
git checkout -b hotfix/security-patch

Trunk-Based Development (Fast Iteration)

Best for: Experienced teams, feature flags, CI/CD maturity

# Everyone commits to main with short-lived branches
git checkout main
git pull origin main
git checkout -b quick-fix

# Small, frequent commits
git commit -m "fix: correct date formatting"
git push origin quick-fix

# Quick PR, merge same day

Commit Message Conventions

Adopt Conventional Commits for readable history and automated changelogs:

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

# Features
git commit -m "feat(auth): add OAuth2 Google login"
git commit -m "feat(api): implement rate limiting"

# Bug fixes
git commit -m "fix(cart): resolve quantity update issue"
git commit -m "fix(ui): correct button alignment on mobile"

# Other types
git commit -m "docs: update API documentation"
git commit -m "style: format code with prettier"
git commit -m "refactor: extract validation logic to utils"
git commit -m "test: add unit tests for payment service"
git commit -m "chore: upgrade dependencies"

Commit Message Best Practices

# Good: Explains what and why
git commit -m "fix(auth): prevent session fixation attack

The previous implementation reused session IDs after login,
creating a security vulnerability. Now generates new session
ID upon successful authentication.

Closes #234"

# Bad: Vague and unhelpful
git commit -m "fixed bug"
git commit -m "updates"
git commit -m "WIP"

Branch Naming Conventions

Consistent naming helps everyone understand branch purpose:

# Feature branches
feature/user-authentication
feature/payment-integration
feature/JIRA-123-add-dashboard

# Bug fixes
fix/login-redirect-loop
fix/cart-total-calculation
bugfix/memory-leak-on-unmount

# Hotfixes (production emergencies)
hotfix/security-patch
hotfix/critical-payment-bug

# Releases
release/v2.1.0
release/2025-q1

# Experiments
experiment/new-ui-framework
spike/redis-caching

Code Review Process

Pull Request Template

<!-- .github/pull_request_template.md -->
## Summary
Brief description of changes

## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update

## How to Test
1. Step one
2. Step two
3. Expected result

## Checklist
- [ ] Tests pass locally
- [ ] Code follows style guide
- [ ] Self-reviewed the code
- [ ] Added necessary documentation
- [ ] No console.log statements

## Screenshots (if UI changes)

Review Guidelines

# Before requesting review
git fetch origin main
git rebase origin/main  # Keep branch up to date
npm test                 # Ensure tests pass
npm run lint             # Check code style

# Address review feedback
git add .
git commit -m "refactor: address review feedback

- Rename variable for clarity
- Add error handling
- Update tests"

Handling Merge Conflicts

Prevention First

# Regularly sync with main
git fetch origin
git rebase origin/main

# Keep branches short-lived (1-3 days ideal)
# Communicate about shared files

Resolution Strategy

# When conflict occurs during rebase
git rebase origin/main
# CONFLICT in src/components/Header.tsx

# Open conflicting file, resolve manually
# Look for conflict markers:
# <<<<<<< HEAD
# Your changes
# =======
# Their changes
# >>>>>>> origin/main

# After resolving
git add src/components/Header.tsx
git rebase --continue

# If things go wrong
git rebase --abort

Visual Merge Tools

# Configure a merge tool
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'

# Use it during conflicts
git mergetool

Protecting Branches

Configure branch protection rules in GitHub/GitLab:

# Required for main branch:
- Require pull request reviews (1-2 approvers)
- Require status checks to pass
  - CI/CD pipeline
  - Tests
  - Lint checks
- Require branches to be up to date
- Restrict force pushes
- Require signed commits (optional)

Git Hooks for Quality

Automate quality checks with pre-commit hooks:

# Install husky
npm install -D husky lint-staged
npx husky init
// package.json
{
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,md}": [
      "prettier --write"
    ]
  }
}
# .husky/pre-commit
npm run lint-staged
npm test -- --passWithNoTests
# .husky/commit-msg
npx commitlint --edit $1

Useful Git Commands for Teams

Staying Current

# Fetch all branches
git fetch --all --prune

# See what's changed on main
git log main..HEAD --oneline

# Interactive rebase to clean up commits before PR
git rebase -i HEAD~3

Investigation

# Find who changed a line
git blame src/utils/auth.ts

# Search commit messages
git log --grep="payment" --oneline

# Find when a bug was introduced
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
# Git will help you find the problematic commit

Recovery

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

# Recover deleted branch
git reflog
git checkout -b recovered-branch abc1234

# Temporarily save changes
git stash
git stash pop

Team Communication

Daily Practices

# Morning sync
git fetch origin
git log --oneline origin/main..main  # See new commits

# Before leaving
git push origin feature/my-branch    # Don't lose work

Async Communication

  • Document complex changes in PR descriptions
  • Tag teammates for specific review areas
  • Use draft PRs for early feedback
  • Link related issues and PRs

Common Pitfalls to Avoid

  1. Long-lived branches: Merge conflicts compound daily
  2. Force pushing shared branches: Destroys teammates' work
  3. Massive PRs: Hard to review, high risk
  4. Vague commits: "fixed stuff" helps no one
  5. Skipping code review: Even small changes benefit from review

Conclusion

A good Git workflow reduces friction and catches problems early. Start with GitHub Flow, add conventions gradually, and automate what you can. The goal is spending time building features, not fighting with version control.

For more development best practices, check out my guides on TypeScript patterns and Node.js development.