Every codebase ships with a second document that nobody designs on purpose: its history. Run the log and you will see which kind your team writes. One kind reads 'fix', 'fix again', 'wip', 'final fix FINAL' — a junk drawer. The other reads like a flight recorder: what changed, why, what it relates to. The difference costs nothing at write time and everything at read time, because the primary reader of a commit message is a stressed human at 2 a.m., six months from now, trying to work out why a line exists before they dare change it.
That reader is very often you.
The message: subject says what, body says why
The mechanics are settled convention. A short imperative subject line — 'Add retry to payment webhook', not 'Added' or 'adds' — that completes the sentence 'if applied, this commit will…'. Then a blank line, then the part that actually matters: the why. The diff already shows what changed; only the message can record what the diff cannot — the bug being fixed, the constraint being honoured, the alternative that was tried and rejected, the link to the issue. 'Increase timeout to 30s' is visible in the code. 'Payment provider's sandbox responds in up to 25s since their March upgrade' is knowledge that exists nowhere else.
One habit upgrades everything: before committing, read the diff and ask what question a stranger would ask about it. Answer that question in the body. If there is genuinely no question — a typo fix, a rename — the subject alone is fine. History should be information-dense, not ceremonious.
The unit: one idea per commit
Message quality is capped by commit shape. A commit that mixes a bug fix, a rename sweep and a drive-by refactor cannot be described honestly in one line — and worse, it cannot be reverted, cherry-picked or bisected as a unit. The discipline is one logical change per commit: the refactor that prepares, then the fix that lands, each standing alone, each buildable. Staging tools exist precisely to split a messy working directory into clean ideas.
This is also what makes git bisect a superpower instead of a slog. Bisect finds the commit that introduced a bug; if that commit is 'wip Friday stuff' touching forty files across three concerns, the search ends in a shrug. If it is one idea with a why in the body, the search ends the investigation outright — the culprit commit is the diagnosis.
History as an instrument
Treated this way, history answers questions nothing else can. Blame on a puzzling line leads to a commit whose body explains the constraint that shaped it. A log filtered to one file tells the story of a module's design pressure over years. A revert becomes safe because the commit is self-contained; a backport becomes a cherry-pick instead of a surgery. Teams even generate honest changelogs straight from subjects — a free by-product of discipline.
None of this requires tooling, process meetings or a style guide longer than a page. It requires believing one thing: the code tells the machine what to do; the history tells humans why it does it. Write for the time traveller. You are them, soon.