Commit Hygiene
Date: 2026-08-17
Commits that each do one thing, with messages explaining why. The whole benefit lands on the person debugging this in eighteen months — frequently you — and the discipline costs almost nothing once the tools are set up.
Commit hygiene is the practice of making commits atomic and messages informative.
The audience is not your reviewer. It’s whoever runs git blame on a strange line in two years and needs to know what the author was trying to do.
Atomic commits
One commit, one logical change. Not one file, and not one day’s work.
BAD
"Various fixes"
→ 14 files, 3 unrelated changes,
one of which broke something
GOOD
"Fix VAT rounding on multi-item orders"
→ the fix, and its test
"Extract price formatting into a helper"
→ pure refactor, no behaviour change
"Add delivery estimate to basket"
→ the feature
Separating refactors from behaviour changes is the highest-value split. A commit that both moves code and changes it is unreviewable — the diff is enormous and the actual change is hidden inside it.
The staging area exists for this. When you’ve done two things at once, commit them separately:
git add -p # stage selected hunks
git commit -m "Fix VAT rounding"
git add .
git commit -m "Add delivery estimate"Messages
The widely-used convention:
Short summary, imperative, under ~50 chars
Why this change was needed, and what it
does about it. Wrap at ~72 characters.
Explain the reasoning, not the mechanics —
the diff already shows what changed.
Refs: PROJ-1234
Imperative mood — “Fix rounding”, not “Fixed” or “Fixes”. It reads as an instruction to the codebase, and it matches git’s own generated messages (“Merge branch…”, “Revert…”).
The body is the valuable part and the part most often absent.
WHAT — visible in the diff
WHY — visible nowhere else
BAD Update timeout to 5000
GOOD Raise API timeout to 5s
Mobile clients on 3G were timing
out on the product search
endpoint, which returns a large
payload. 3s was set arbitrarily
when the endpoint was smaller.
Reverting if p99 latency worsens.
The second one answers the question git blame produces, which the first does not.
Conventional commits
A structured format many teams adopt:
feat(basket): add delivery estimate
fix(checkout): correct VAT rounding
refactor(pricing): extract formatter
docs(readme): update setup steps
chore(deps): bump eslint to 9.x
For: machine-readable, so changelogs and version bumps can be generated automatically — Semantic Versioning.
Against: the prefix carries little for a human reader, and teams frequently adopt the format without the automation that justifies it.
Adopt it if you’re generating releases from it. Otherwise it’s ceremony.
Work-in-progress is fine
The discipline is about what lands, not about how you work:
git commit -m "wip"
git commit -m "wip 2"
git commit -m "actually works"
# then, before opening the PR
git rebase -i main # squash into
# coherent commitsCommit freely while working; tidy before sharing — Rewriting History.
What it enables
The reason to bother, concretely:
git blameanswers “why” rather than “who”git bisectnarrows to something small — Git Bisectgit revertworks cleanly, because one commit is one change- Review is possible. A reviewer reading four clear commits does better than one reading a 900-line diff — Code Review
- Cherry-picking a fix to a release branch is one commit, not an archaeology exercise
The realistic bar
Not every commit needs an essay. A one-line message is fine for an obvious change; a body is needed when the reasoning isn’t in the diff.
The test: would this message help someone who has just run git blame on this line and has no other context? If the change is self-evident, one line. If someone might reasonably ask “why on earth is it done this way” — write the paragraph.