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 featurefix- Bug fixdocs- Documentation changesstyle- Formatting, no logic changerefactor- Code restructure, no behavior changeperf- Performance improvementtest- Adding or fixing testschore- Build process, dependency updatesci- 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:
mainis always deployable- All work happens in feature branches
- Branches merge to
mainvia PR - Deploy from
mainautomatically 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
| Size | Lines Changed | Expectation |
|---|---|---|
| XS | < 10 | Immediate review |
| S | 10-100 | Same day |
| M | 100-500 | 1-2 days |
| L | 500+ | 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
.envfiles 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
- Commit early, commit often - small commits are easier to review and revert
- Never force-push to shared branches - coordinate or use
--force-with-lease - Delete merged branches - keep the repo tidy
- Pull before you push - avoid unnecessary merge commits
- Write the commit message you'd want to read - your future self will thank you
