Dev / IT6 min read

How to Write Good Git Commit Messages (With Examples)

Write commit messages that your future self will thank you for — the 50/72 rule, imperative mood, Conventional Commits format, when to use the body, and examples of bad vs good commit messages.

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 #247

Conventional 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 latest

Bad vs Good: Real Examples

❌ Bad✅ Good
fix bugFix null pointer on user profile when avatar is unset
update codeRefactor payment service to use strategy pattern
WIPfeat(checkout): add Apple Pay support (WIP — not ready)
asdf(This shouldn't exist — squash before pushing)
changesUpdate Nginx config to enable HTTP/2
fixed the thingFix 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 --amend to fix the last commit before pushing
← Back to all articles