How to write commit messages your future self can search, plus conventional commits
Run git log --oneline on almost any project and you'll find a stretch like this.
a1f3c2e wip
9be07d1 fix
3c44a9f more fixes
e02b8d1 asdf
7d1f6aa final changes for real
Every one of those commits was obvious to the person who wrote it, on the day they wrote it. A year later, during a git bisect at 11 PM, none of them mean anything.
These are the git commit message best practices that fix that, each shown with a before and after rewrite, plus how conventional commits fit on top.
The seven habits that matter
- Separate the subject from the body with a blank line.
- Keep the subject around 50 characters, and wrap the body near 72.
- Capitalize the subject and skip the trailing period.
- Write the subject as a command, as in "Add", "Remove" or "Rename".
- Say what changed and why. The diff already shows how.
- Make one logical change per commit.
- Reference the issue or ticket in the body or footer, not as the whole message.
A handy test for the subject is to finish the sentence "If applied, this commit will..." with it. "If applied, this commit will add rate limiting to the login endpoint" reads well. "If applied, this commit will fixed stuff" doesn't.
Before and after rewrites
Vague subject
fix
Prevent duplicate invoices when checkout is retried
The first tells you nothing. The second says what broke, from the outside, so anyone searching the log for "invoice" finds it.
Subject that's really a diary entry
Finally got the date picker working after hours of pain
Fix date picker ignoring the locale's first day of week
Feelings belong in the team chat. The log is for facts.
Everything in one commit
update settings page, fix typo in readme, bump eslint
Split it into three commits.
Add timezone selector to settings page
Fix typo in README install steps
Bump eslint from 8.56 to 8.57
Now each one can be reverted on its own, and blame points to the right change.
Missing the why
Change cache TTL to 300
Lower product cache TTL to 5 minutes
Prices stayed stale for up to an hour after edits in the admin
panel, which caused support tickets during sales. Five minutes
keeps database load low while letting price changes show up
soon after they're saved.
Refs #812
The subject tells you what. The body tells the next developer not to raise it back to an hour without thinking.
Where conventional commits fit
Conventional commits is a spec for a structured subject line. It adds a type and an optional scope in front of the description.
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
Common types are feat, fix, docs, refactor, test, perf, build, ci and chore. A ! after the type, or a BREAKING CHANGE footer, marks a breaking change.
Here's the cache example in that style.
fix(cache): lower product cache TTL to 5 minutes
Prices stayed stale for up to an hour after admin edits.
Refs #812
And a breaking one.
feat(api)!: require an API key for all v2 endpoints
BREAKING CHANGE: anonymous requests to /v2 now return 401.
Clients must send the X-Api-Key header.
Most teams using the convention write lowercase descriptions, which clashes with habit 3. Pick one rule per repo and stick to it.
Why teams adopt it
The structure is machine readable. Tools can generate changelogs from feat and fix commits and work out the next semantic version. A fix maps to a patch, a feat to a minor and a breaking change to a major.
It also nudges people to think in single changes. If you can't pick one type for a commit, it's probably two commits.
When it's overkill
On a solo side project with no releases, the type prefix can be noise. The seven habits above matter more than the prefix. A clear plain subject beats a typed prefix followed by "stuff" every time.
Make it easy to do right
Set a real editor for commits so you're not cramming everything into -m. Add a commit template with git config commit.template ~/.gitmessage that reminds you of the subject and body shape. If your team uses conventional commits, run commitlint in a hook or a CI step so the format gets checked for you.
Finally, read your subject line cold, the way a stranger to the code would. If it only makes sense with context in your head, rewrite it. Rephrase AI keeps what a sentence means while changing the words, and you can line the result up against your first draft before you commit. Its blog also has a short post on ways to rephrase text for clarity, and the same ideas work on a commit body. Your teammates, and you in a year, will be able to search the history and find what they need.
Top comments (0)