Commit messages are the design doc
On blaming a line and finding the word `fix` with my own name on it, what actually belongs in the body, and the moment of maximum context you're throwing away.
I ran git blame on a strange-looking line in a template a few weeks ago. Four nested conditions around a redirect, no comment, no obvious purpose, the kind of thing you delete on a tidy-up Friday.
The commit message was fix. Author: me. April 2016.
The ticket it referenced didn’t reference anything, because the number in it belonged to a project management tool the agency stopped paying for in 2017 and the data went with it. So there was no record anywhere of why four nested conditions were wrapped around a redirect, and the only person who had ever known was me, and I had written down the word “fix.”
I left it in, obviously. You always leave it in. And then the code has a piece of superstition in it forever, which is how codebases get haunted.
The only document welded to the code
Last year I wrote a fairly enthusiastic post about READMEs, and I still mean it, but I should be honest about the weakness of every document I described in it: they all drift. The README describes the build until the build changes. The wiki page is accurate until someone refactors. The architecture doc was true in March.
Drift happens because the document and the code are two separate things that have to be updated together by a person who has to remember to, and that person is tired.
A commit message can’t drift. It’s attached to a specific diff, permanently, and that diff never changes. The message describes exactly that change, and if the code is later replaced, the new code brings its own message. There’s no maintenance burden because there’s nothing to maintain.
Every document about a codebase starts drifting the moment you save it. A commit message is welded to its diff for as long as the repository exists.
That’s an unusual property and I don’t think we take much advantage of it.
Written at the moment of maximum context
The other thing about the commit message, and it’s the thing that ought to make it the best document in the project: you write it at the single instant when you know the most about the change you’re describing.
You have just spent two hours on it. You know what you tried first. You know the thing that looked like it should work and didn’t, and why. You know which of the four conditions is essential and which is defensive. You know what you were worried about.
Nobody, ever again, including you, will know as much about that change as you do at that moment. And the standard practice at that moment is to type “fix” and go home.
I do understand why. It’s the end of the task, the interesting part is over, and the message feels like paperwork. But it’s the only moment the information exists and it costs about 90 seconds to catch it.
What goes in the body
The diff already says what changed. So the body says the things the diff can’t.
What was actually wrong, in terms of the symptom rather than the code. “Orders over $1,000 were showing the wrong sales tax on the confirmation page” is findable in three years by someone searching for the words a human would use.
Why this approach and not the obvious one. This is the highest-value sentence and it’s nearly always missing. If the straightforward fix didn’t work, say so, or the next person will try it, because it’s straightforward.
The constraint that isn’t visible in the code. The redirect that looks wrong because the client printed the old URL on 50,000 leaflets. The delay that exists because a payment provider’s sandbox behaves differently from live. These are the ones that get deleted by a well-meaning person, and the commit message is the only place they’d ever be found.
What you’re not sure about. “I think this is the right layer for this but I couldn’t see a cleaner way without touching the checkout” is genuinely useful to the next reader and costs you nothing but a small amount of pride.
Guard the delivery-date lookup against bank holidays
Orders placed on the Friday before a federal holiday were quoting a
next-working-day date that fell on the holiday itself, so a handful of
customers were promised a Monday that didn't exist.
The obvious fix is to add the dates to the existing weekend check, but
that check runs on the client's timezone and the holiday list is
England-only, and they ship to Scotland where the August one differs.
So this pulls the list per region instead, which is more code than it
looks like it should be.
The list is hardcoded to 2019. It will need updating and there is
nothing that will remind us.
That last paragraph is the one I’d fight for. It’s an admission of a known future problem, recorded in the one place that will still exist when the problem arrives.
The imperative, since everyone asks
Subject line in the imperative: “Add,” “Fix,” “Remove,” not “Added” or “Fixes.”
The reason isn’t aesthetic. Git writes messages itself in that voice, so your log ends up half “Merge branch ‘x’” and half “Fixed the thing,” and it reads badly. The test people quote is that the subject should complete the sentence “if applied, this commit will…,” which sounds like a rule someone invented afterward and is still the most useful thing to hold in your head.
Keep it short enough to read in a log listing, put a blank line after it because git treats the first paragraph as the subject, and wrap the body at about 72 characters because the tools that display it don’t wrap for you.
Three habits I dropped
Linking to a ticket as the entire message. “See PROJ-4471.” That was fine until the day the ticket system was replaced, and every project I’ve worked on has replaced its ticket system, usually more than once. Link to it by all means, and put enough in the message that it stands alone.
Batching a day’s work into one commit. This is the same argument as the bisect post last month, and it’s really the same argument as this whole post: a commit that does four things can’t have a message that explains why, because there are four whys and they’re interleaved.
And describing the diff in prose. “Changed the value of the timeout from 30 to 60.” I can see that. I could see it before I read your sentence.
I don’t do this consistently
I’d like to say I do and I don’t. My history has plenty of “fix,” plenty of “tweak,” one “argh” that I found while writing this, and a depressing number of one-line messages on commits that touch nine files.
But the ratio has improved a lot since I started thinking of the message as being addressed to a specific person, and the person is whoever runs git blame on a line I wrote and needs to decide whether they’re allowed to delete it.
That person is usually me. Which is either motivating or bleak, depending on the week.