Git Workflow Best Practices for Modern Development Teams

Git Workflow Best Practices for Modern Development Teams

Git is powerful, but with great power comes... the potential for a messy repository. Here's how to use Git effectively in a team environment without losing your mind.

Branching Strategies

Git Flow

Good for projects with scheduled releases:

main (production)
├── develop (integration)
    ├── feature/user-auth
    ├── feature/payment
    └── release/v1.2.0
# Start a feature
git checkout develop
git checkout -b feature/user-auth

# Finish feature
git checkout develop
git merge --no-ff feature/user-auth
git branch -d feature/user-auth

# Create release
git checkout -b release/v1.2.0 develop
# Make release-specific changes
git checkout main
git merge --no-ff release/v1.2.0
git tag -a v1.2.0

GitHub Flow

Simpler, great for continuous deployment:

main (always deployable)
├── feature/add-search
├── fix/login-bug
└── feature/dark-mode
# Create feature branch
git checkout -b feature/add-search

# Make changes, commit often
git add .
git commit -m "Add search functionality"

# Push and create PR
git push origin feature/add-search
# Create PR on GitHub

# After PR approval
# Merge and delete branch

Trunk-Based Development

For high-velocity teams:

main (trunk)
├── short-lived feature branches (< 1 day)
└── feature flags for incomplete features

Commit Best Practices

Write Good Commit Messages

Follow the Conventional Commits standard:

<type>(<scope>): <subject>

<body>

<footer>

Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation
  • style: Formatting, no code change
  • refactor: Code restructuring
  • test: Adding tests
  • chore: Maintenance

Examples:

# Good
git commit -m "feat(auth): add Google OAuth login"
git commit -m "fix(api): handle null response in user endpoint"
git commit -m "docs: update installation instructions"

# Bad
git commit -m "fixed stuff"
git commit -m "updates"
git commit -m "asdfasdf"

Commit Early, Commit Often

# Make small, logical commits
git add src/auth/login.js
git commit -m "feat(auth): add login form validation"

git add src/auth/oauth.js
git commit -m "feat(auth): integrate Google OAuth"

git add tests/auth.test.js
git commit -m "test(auth): add OAuth integration tests"

Atomic Commits

Each commit should be a single logical change:

# ❌ Bad: Multiple unrelated changes
git add .
git commit -m "Add login, fix bug, update docs"

# ✅ Good: Separate commits
git add src/auth/
git commit -m "feat(auth): add login functionality"

git add src/api/user.js
git commit -m "fix(api): handle null user response"

git add README.md
git commit -m "docs: update API documentation"

Branch Management

Naming Conventions

feature/description    # New features
fix/description       # Bug fixes
hotfix/description    # Urgent production fixes
refactor/description  # Code refactoring
docs/description      # Documentation changes
test/description      # Test updates

# Examples
feature/user-authentication
fix/login-redirect-bug
hotfix/payment-gateway-timeout
refactor/database-queries
docs/api-endpoints
test/integration-tests

Keep Branches Short-Lived

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

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

Delete Merged Branches

# Delete local branch
git branch -d feature/my-feature

# Delete remote branch
git push origin --delete feature/my-feature

# Prune deleted remote branches
git remote prune origin

# List all merged branches
git branch --merged main | grep -v "main"

Pull Request Best Practices

Before Creating a PR

# 1. Update your branch
git fetch origin
git rebase origin/main

# 2. Run tests
npm test

# 3. Check for conflicts
# Resolve any conflicts

# 4. Push
git push origin feature/my-feature

PR Description Template

## Description
Brief description of what this PR does.

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

## Testing
Describe how you tested this:
- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] Manual testing completed

## Screenshots (if applicable)
Add screenshots for UI changes.

## Checklist
- [ ] My code follows the project's style guidelines
- [ ] I have performed a self-review
- [ ] I have commented my code where necessary
- [ ] I have updated the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective
- [ ] New and existing unit tests pass locally

Review Your Own PR First

# Before submitting, review your changes
git diff main...feature/my-feature

# Check file changes
git diff --name-only main...feature/my-feature

Code Review Guidelines

As Author

  • Keep PRs small (< 400 lines)
  • Provide context in description
  • Respond to feedback promptly
  • Don't take feedback personally

As Reviewer

  • Be kind and constructive
  • Ask questions, don't make demands
  • Approve when satisfied, not perfect
  • Review promptly (within 24 hours)
# Checkout PR locally for testing
git fetch origin pull/123/head:pr-123
git checkout pr-123
npm install
npm test

Useful Git Commands

Stash Changes

# Save work in progress
git stash save "WIP: working on feature"

# List stashes
git stash list

# Apply stash
git stash apply stash@{0}

# Apply and delete stash
git stash pop

# Delete stash
git stash drop stash@{0}

Cherry Pick

# Apply specific commit to current branch
git cherry-pick abc123def

Revert

# Undo a commit (creates new commit)
git revert abc123def

# Undo multiple commits
git revert HEAD~3..HEAD

Reset

# Undo commits (destructive!)
git reset --soft HEAD~1   # Keep changes staged
git reset --mixed HEAD~1  # Keep changes unstaged (default)
git reset --hard HEAD~1   # Discard changes completely

Clean Up History

# Interactive rebase
git rebase -i HEAD~5

# Squash commits in interactive rebase:
# pick abc123 First commit
# squash def456 Second commit
# squash ghi789 Third commit

Find Bugs

# Binary search to find bad commit
git bisect start
git bisect bad HEAD
git bisect good abc123

# Test each commit
# Mark as good or bad
git bisect good
# or
git bisect bad

# When found
git bisect reset

Search

# Search in commit messages
git log --grep="bug fix"

# Search in code
git grep "function login"

# Search in history
git log -S "login" --source --all

Git Hooks

Automate checks:

# .git/hooks/pre-commit
#!/bin/sh
npm test
npm run lint

# Make executable
chmod +x .git/hooks/pre-commit

Use Husky to share hooks:

{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged",
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  }
}

.gitignore Best Practices

# Dependencies
node_modules/
vendor/

# Build outputs
dist/
build/
*.min.js

# Environment
.env
.env.local
.env.*.local

# IDE
.vscode/
.idea/
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# Logs
*.log
logs/

# Testing
coverage/
.nyc_output/

Merge vs. Rebase

Use Merge When

  • Working on public/shared branches
  • Want to preserve exact history
  • Following Git Flow
git merge feature/my-feature

Use Rebase When

  • Updating feature branch with main
  • Want clean, linear history
  • Following GitHub Flow
git rebase main

Git Aliases

Make your life easier:

# Add to ~/.gitconfig
[alias]
    st = status
    co = checkout
    br = branch
    ci = commit
    unstage = reset HEAD --
    last = log -1 HEAD
    visual = log --graph --oneline --all
    amend = commit --amend --no-edit

Pro Tips

  1. Commit often - Easy to squash later
  2. Pull with rebase - git pull --rebase
  3. Sign commits - Use GPG for verified commits
  4. Use .gitattributes - Normalize line endings
  5. Keep main deployable - Always
  6. Delete merged branches - Regularly
  7. Use tags - Mark releases
  8. Write good docs - In README and CONTRIBUTING

Common Pitfalls

Force Push Safely

# Never force push to shared branches
# If you must, use --force-with-lease
git push --force-with-lease

Merge Conflicts

# Accept theirs
git checkout --theirs path/to/file

# Accept ours
git checkout --ours path/to/file

# Use merge tool
git mergetool

Keep Learning

Need help setting up a Git workflow for your team? Get in touch.