Advertisement

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.

Advertisement

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.

Advertisement

Atomic commits: one logical change per commit, not one file save per commit

An atomic commit contains exactly one coherent, complete logical change — nothing more, nothing left half-finished — which is a genuinely different granularity than committing every time a file happens to be saved, or waiting to commit until an entire day's disconnected work has piled up together. The practical test for whether a commit is atomic: could its message describe the change in one clear sentence without needing 'and' to join two unrelated things together, and could the change be reverted cleanly on its own without also reverting something unrelated that happened to be bundled into the same commit.

Why `git bisect` is the tool that makes commit quality suddenly, concretely matter

`git bisect` automates finding the exact commit that introduced a regression by binary-searching through history, running a test or check at each candidate commit — and its usefulness depends entirely on the commits actually being bisectable, meaning each one leaves the codebase in a genuinely working, testable state on its own; a history full of commits that do not build or do not pass tests in isolation, common when commits are just arbitrary save points rather than atomic changes, makes bisecting far less useful, since a large fraction of the candidate commits it lands on cannot actually be tested at all.

Advertisement

Squashing before merge: cleaning up the story without losing it

The messy, iterative sequence of commits an author naturally accumulates while actually working on a change — a false start, several `wip` commits, a couple of typo fixes — is genuinely useful during that work but rarely useful to preserve permanently in the shared history afterward; squashing that sequence down into one or a small handful of clean, atomic commits before merging keeps the shared history readable for `git blame` and `git bisect` alike, while the original, messier history remains available in the pull request's own review thread for anyone who specifically wants to see how the work actually evolved along the way.

`git blame` as a direct, everyday consumer of everything discussed in this pair of articles

Running `git blame` on a confusing line of code and finding the commit message 'fix bug' with no further explanation, attached to a change that touched forty unrelated files, is the direct, concrete failure this pair of articles is describing — and finding instead a small, atomic commit with a clear message explaining exactly why that specific line exists is the direct, concrete payoff of everything covered throughout, which is worth remembering as the actual audience for a well-written commit message: not an abstract ideal reader, but a real future engineer, quite possibly the same person who wrote it, staring at exactly this line months later trying to understand it.

Why a linear, readable history is worth the extra step of an interactive rebase

A project's commit history read front to back should ideally tell a coherent story of how the codebase actually evolved, one logical step at a time, and a tangle of merge commits interleaving several branches' work in an order that has little to do with the actual logical sequence of changes undermines that story considerably — rebasing a feature branch onto the latest main before merging, producing a clean, linear sequence of atomic commits, is the concrete practice that keeps history readable as a story rather than a maze, at the cost of the small extra discipline of resolving conflicts during the rebase rather than only at merge time.

Why history worth reading is a gift to a future engineer who is not the author

The person most likely to eventually benefit from a genuinely well-kept commit history is not its original author, who already remembers the reasoning without needing to consult it, but some future engineer — quite possibly one who joined the team long after the commit was written — trying to understand a decision they had no part in making, with nothing to go on but whatever was actually written down at the time; treating commit history with that specific, unfamiliar future reader in mind, rather than writing purely for one's own present-moment convenience, is the mindset shift underlying every practice this pair of articles describes.

Why a commit that reverts a previous one deserves its own clear explanation

A revert commit is exactly as important to explain clearly as any other, and a bare 'revert previous commit' message forces a future reader back to the original commit's own message to understand why it existed and then to guess at why it was later undone — a good revert message states directly what specifically went wrong with the original change, which is often more useful context than the original commit's own message, since it captures a lesson learned that the original commit could not have known about at the time it was written.

Why a well-kept history is a form of institutional memory that outlasts any one person

A team's actual, functioning institutional memory is not the wiki page nobody updates or the onboarding document that goes stale within a year, it is very often the commit history itself, since it is generated as a direct byproduct of the work actually happening rather than a separate documentation effort someone has to remember to maintain — a codebase with a genuinely well-kept history retains a working memory of its own evolution long after every person who wrote any individual part of it has moved on to something else.

Why a commit's diff size is a rough proxy for how carefully it was likely reviewed

Beyond the review-thoroughness argument made elsewhere in this library regarding pull request size, commit size specifically affects how useful `git log -p` and similar history-browsing tools are after the fact — a history made of small, atomic commits lets someone scanning past changes read each one quickly and understand it in isolation, while a history of enormous commits turns the same kind of historical scan into wading through diffs too large to meaningfully absorb one at a time, which is a second, independent reason atomic commits pay off beyond the review-quality argument already covered.

Why a project's very first commits are worth reading when joining a new team

Early commits in a project's history frequently reveal foundational decisions and the reasoning behind them more directly than any current documentation does, since those decisions were often made explicit at the moment they were first needed, before later abstraction layers obscured the original reasoning — a new team member who takes the time to read through a project's early history, rather than only ever looking at its current state, often gains context that no onboarding document alone would have conveyed as directly.

Why history quality is a leading indicator of a team's overall engineering discipline

A team that consistently writes atomic, well-explained commits is very often, not coincidentally, the same team that writes thorough tests and clear code comments, because all of these habits stem from the same underlying value: caring about the experience of whoever reads the work later, including one's own future self — commit history quality is a small, easily observable signal of that broader discipline, which is why an experienced engineer joining a new team often reads a project's commit log early, as a quick, informal gauge of the surrounding engineering culture before writing a single line of code.

Why treating history as disposable eventually costs more than it saves

A team that periodically squashes an entire long-lived branch's history down to a single commit purely for tidiness, discarding the individual atomic steps in the process, trades a small amount of visual neatness in the log for the permanent loss of exactly the granular blame-and-bisect information this pair of articles has argued is worth preserving — tidiness is a reasonable goal, but it is worth weighing against what specifically gets thrown away to achieve it.