A git history is a log of your project's reasoning. Good commit messages let you understand why code changed, not just what changed — and git blame becomes a useful tool instead of a source of frustration.
The Golden Rule
The commit message should complete this sentence: "If applied, this commit will ___________"
✅ "If applied, this commit will: fix the login redirect for OAuth users"
✅ "If applied, this commit will: add rate limiting to the auth endpoints"
❌ "If applied, this commit will: fix stuff"
❌ "If applied, this commit will: wip"
❌ "If applied, this commit will: asdfgh"The 50/72 Rule
- Subject line: ≤ 50 characters — fits in GitHub's commit list, git log --oneline
- Body lines: ≤ 72 characters — readable in terminals and email clients
- Blank line between subject and body — many tools depend on this
Imperative Mood for the Subject
# Use the imperative (command) form — like git itself does
✅ Add user authentication
✅ Fix memory leak in image processor
✅ Update dependencies to latest versions
✅ Remove deprecated API endpoints
❌ Added user authentication (past tense)
❌ Fixing memory leak (present participle)
❌ Updates (too vague)
❌ auth stuff (too vague)When to Write a Body
Add a body when the "what" is obvious but the "why" is not:
Fix login redirect after OAuth callback
Previously, users were redirected to /dashboard after OAuth login
regardless of where they were trying to go. Now we store the intended
URL in the session before the OAuth flow and redirect there on success.
Closes #247Conventional Commits Format
A popular convention used with semantic versioning tools:
type(scope): subject
Types:
feat: New feature → triggers minor version bump
fix: Bug fix → triggers patch version bump
docs: Documentation only
style: Formatting, whitespace (no logic change)
refactor: Code change that neither fixes a bug nor adds a feature
perf: Performance improvement
test: Adding or updating tests
chore: Build process, tooling, dependencies
Examples:
feat(auth): add OAuth2 Google login
fix(payments): handle Stripe webhook retry correctly
docs(api): add rate limiting documentation
perf(images): lazy load below-the-fold images
chore(deps): update all dependencies to latestBad vs Good: Real Examples
| ❌ Bad | ✅ Good |
|---|---|
| fix bug | Fix null pointer on user profile when avatar is unset |
| update code | Refactor payment service to use strategy pattern |
| WIP | feat(checkout): add Apple Pay support (WIP — not ready) |
| asdf | (This shouldn't exist — squash before pushing) |
| changes | Update Nginx config to enable HTTP/2 |
| fixed the thing | Fix session expiry not resetting after re-login |
Practical Tips
- Write the commit message before you start coding — it clarifies what you're about to do
- One commit = one logical change. Don't combine unrelated changes.
- Reference issue numbers:
Closes #123,Fixes #456 - Squash cleanup commits before merging a PR:
git rebase -i HEAD~5 - Use
git commit --amendto fix the last commit before pushing