The Subject Line
- Use imperative mood: "Add feature" not "Added feature"
- Keep it under 50 characters
- Don't end with a period
- Capitalize the first word
The Body (When Needed)
For non-trivial changes, add a blank line then explain:
- What changed
- Why it changed
- How it affects existing behavior
Reference Issues
Link to the relevant issue or ticket: Fixes #142 or Closes #87. This creates traceability between code and requirements.
Bad vs Good Examples
Bad: "fixed login bug"
Good: "fix(auth): prevent token refresh race condition on concurrent requests"
Bad: "updated stuff"
Good: "refactor(api): extract validation logic into middleware layer"
The Golden Rule
Write commit messages as if you're explaining the change to someone who hasn't seen the code. Your future self (and your teammates) will thank you.
Curious how your code and commit practices stack up? Our code roast tool reviews your code quality, structure, and best practices — including naming conventions and documentation patterns.
Commit Message Anti-Patterns to Avoid
- "wip" and "final" and "final2". These tell your future self nothing about which state the code is in. If a commit is still work in progress, keep it on a branch you can squash.
- Mixed concerns. A commit that changes the auth flow, renames three files, and bumps a dependency makes archaeology painful. One logical change per commit.
- Fixing forward silently. When you fix a bug three commits later, the original line still looks correct to anyone reading history. Reference it in the message:
fix(cart): correct shipping calc introduced in 3f2a9c.
- Message that reproduces the diff. "Update function" adds nothing the diff already shows. Explain the why — what changed is visible in the code, why it changed is not.
The 30-Second Commit Checklist
- Type + optional scope present (feat, fix, refactor, docs, test, chore)
- Imperative mood, under 50 characters, no trailing period
- Body explains why and any behavior change, not what
- Related issue or PR referenced
- Co-authored or breaking-change noted (e.g.,
BREAKING CHANGE:)
Should You Use Behavioral vs. Conventional Commits?
Conventional Commits gives you machine-readable history: auto-generated changelogs, semantic versioning, and blame tools all parse feat vs fix. If your team already uses changelog generators or release automation, standardize on it. If not, a simpler "imperative verb + short subject + reason in body" convention still beats ad-hoc messages. Pick one, document it in your repo, and make it a review gate — consistency matters more than the specific convention.
Commit Message Questions, Answered
Should every commit be one small change?
Ideally yes, but the practical rule is stronger: every commit should describe one coherent unit of work that leaves the codebase in a working state. Tiny commits are only useful if their messages stay meaningful. The enemy is not size; it is incoherence: a commit that mixes three unrelated changes is a history-time bomb. It makes reverts impossible and blame archaeology painful.
How long can a commit message be?
Subject under 50 characters, body as long as it needs to be. But if the body exceeds a few paragraphs, the change is probably too large. A long body is a signal to split the work. If you must document a complex decision, that belongs in code comments or a design doc, referenced from the commit.
Should I rewrite history with git rebase?
On your own branch before pushing: yes, freely. Squash noisy WIP commits into coherent ones and fix sloppy messages. On a shared branch or after a merge: no, because rewriting shared history breaks everyone else's view of the repo. The rule is simple: clean up before you push, never after.
Do commit messages really matter for solo projects?
Yes, more than anywhere else. In six months you will navigate that history to remember why a decision was made, and the discipline transfers directly to team work. The cost is seconds per commit; the payoff is hours of archaeology saved when something subtle breaks.
Try Our Code roast tool
Get an honest, multi-angle review of your code in 15 seconds.
Try It Now