Developer Tools

Git history should explain the decision

6 min read

Git history is one of the cheapest places to preserve engineering memory.

I do not mean every commit needs to become a diary entry. Most do not. But a commit message should usually answer the question future-you will ask when something breaks: why did this change happen?

The diff already shows what changed. The message should explain the decision.

That distinction matters because debugging often starts in the past. A test starts failing. A dependency behaves differently. A feature flag has a strange default. A helper has a weird branch that nobody wants to touch. You run git blame, find the commit, and hope the message says more than fix stuff.

When it does, you save time. When it does not, the codebase makes you re-litigate an old decision with less context.

the subject line is a label

The first line should be a useful label, not a complete explanation.

I like subjects that name the area and the action:

auth: preserve invite token through callback
blog: remove generated prose from model-watch posts
forms: add idempotency key to project creation
evals: split policy failures by refusal type

That makes scanning history easier. It also makes git log --oneline useful when the repository has moved on.

The subject should not carry too much ceremony. If the change is a small typo fix, docs: fix typo in install step is enough. If the change alters behavior, the body should earn its keep.

the body should capture the decision

The body is where the useful context lives.

A good commit body answers a few boring questions:

  • What problem did this solve?
  • Why this approach?
  • What alternatives were rejected?
  • What risk remains?
  • How was it verified?

It does not need all five every time. The point is to preserve the context that will disappear from memory.

Example:

forms: add idempotency key to project creation

Project creation could duplicate records when the client retried after a
timeout. The UI already disables the button while submitting, but that does
not protect against refreshes, network retries, or repeated requests from an
old tab.

Use a client-generated idempotency key and store it with the creation attempt.
This keeps legitimate second projects possible because the key is scoped to
one submit intent, not the project name.

Verified with the invite retry regression test and the existing project
creation suite.

That message is longer than the diff summary. Good. The diff cannot tell you why the key is scoped to submit intent instead of project name.

bad messages create archaeology

Some commit messages are actively expensive:

fix
updates
wip
refactor
cleanup
address comments
changes

Those subjects might be honest in the moment, but they are almost useless later. They force the reader to open the diff for every clue. Worse, they erase the decision boundary. Was this cleanup required for the feature, or was it opportunistic? Was the refactor behavior-preserving? Were comments addressed by changing code, tests, docs, or all of the above?

Even a slightly better message helps:

parser: preserve quoted commas in csv import

Now the future reader knows what to look for.

commits should be shaped for review

Good history starts before the commit message. It starts with commit shape.

A commit that changes formatting, rewrites helpers, updates tests, and fixes behavior is hard to explain because it is several decisions. Sometimes that is unavoidable. Usually it is a sign the work should be split.

I want commits that answer one review question at a time:

test: add duplicate-submit regression coverage
forms: add idempotency key to project creation
ui: show retry state after project creation timeout

That sequence tells a story. The test captures the bug. The backend fix prevents duplication. The UI fix improves recovery. If one part has to revert, the boundary is visible.

Clean history is not about making the graph pretty. It is about making review, revert, and blame less miserable.

generated work needs better receipts

Agent-written code makes commit messages more important, not less.

Agents can produce large diffs quickly. They can also mix real fixes with accidental edits, generated artifacts, stale assumptions, or broad cleanup that was never requested. A commit message is one of the places where the human can reassert intent.

For agent-assisted work, I want the message to say what was verified:

blog: rewrite fine-tuning post from recovered outline

Replace the generated agent-control prose with a topic-specific article about
baselines, datasets, splits, checkpoints, and regressions. Preserve frontmatter.

Verification:
- pnpm markdown:check src/content/blog/fine-tuning-is-not-a-shortcut.mdx
- banned phrase scan
- lowercase heading scan

That receipt is useful because the diff alone may look like a wall of prose. The message tells the next reviewer what constraints mattered.

do not put everything in git history

Git history is not the only place for decisions.

Architecture decisions, product requirements, incident findings, and release notes often deserve their own documents. A commit message can point to them. It should not pretend to replace them when the decision is large.

The sweet spot is local context: why this implementation, why this migration order, why this default, why this fallback, why this test exists.

If the explanation is longer than the patch, that may still be fine. If the explanation describes a company-wide direction, write an ADR or design note and link it.

the future reader is probably annoyed

The person reading old git history is rarely relaxed. They are probably debugging something, preparing a revert, trying to understand a regression, or figuring out why a weird choice exists.

Write for that person.

They do not need a perfect essay. They need the missing reason. They need to know whether the change was a workaround, a migration step, a product decision, a security fix, or a temporary compromise that nobody removed.

A commit message is cheap when the work is fresh. It is expensive to reconstruct later.

That is enough reason to write the decision down.

Jeremy London

About Jeremy London

Engineering leader and builder in Denver. I write about AI platforms, agents, security, reliability, homelab infrastructure, and the parts of engineering work that have to survive production.