Can I delete this?
I ran git blame on a line in a client’s template last week. Four nested conditions wrapped around a redirect, no comment above it, nothing near it that explains it. The kind of line you delete on a quiet afternoon when you’re feeling tidy.
The message was one word. fix. Author: me, 2017.
There was a ticket number in the branch name, which felt like something, until I remembered that the tool those numbers lived in got replaced the year after and the old data didn’t come with it. So the entire surviving record of why a redirect on a live store needs four conditions around it was a three-letter word I typed myself, and the only person who ever knew the answer was me. I don’t.
I left it in, of course. You always leave it in. And now that template has a line in it nobody is allowed to touch, including me, on the strength of a note that says nothing, which is how a codebase ends up carrying little bits of folk magic around.
Every other document goes stale
Last year I wrote something enthusiastic about READMEs and I still mean all of it. But every document in that post shares one weakness, which is that it describes the code from the outside. The README is right until the build changes. The wiki page is right until somebody moves a folder. The architecture doc was right in March.
That happens because the document and the code are two separate objects kept in agreement by a person who has to remember to do it, and that person is at the end of a long day with something else they’d rather be doing.
A commit message can’t go stale. It’s bound to one diff, and that diff is finished forever, so whatever the message says it says about exactly those lines as they were at that moment. When somebody replaces the code, the replacement arrives carrying a message of its own. There’s nothing to maintain because there’s nothing that could fall out of step.
That’s a genuinely unusual property for a document to have. I don’t think most of us get much out of it, myself very much included.
The moment you know the most
The other thing about the commit message, and the thing that ought to make it the best writing in the whole project, is that you produce it at the exact instant you understand the change better than you ever will again.
You’ve been on it for two hours. You know what you tried first. You know the obvious approach and the specific way it fell over. You know which of those four conditions is doing real work and which one you added because you got nervous about something. You know what you were afraid would happen on a Saturday.
Nobody is going to know as much about that change as you do right then, and that includes you a year from now. So it’s a little funny that the standard move in that moment is to type “fix” and go home.
I understand the pull, because I feel it too. The task is over, the part I liked happened an hour ago, and writing it up feels like homework assigned on top of the work. But the information only exists for about as long as it takes to make a coffee, and then it’s gone for good.
What goes in the body
The diff already says what changed, in more detail than you could write out and with better accuracy. So the body is for the things the diff has no way to say.
The symptom, in the words a person would actually use. “Orders over $1000 were showing the wrong sales tax on the confirmation page” is findable in three years by somebody who knows nothing except that the tax is wrong. “Fix tax calc” is findable by nobody.
Why this approach and not the obvious one. This is the most useful sentence available to you and it’s the one that’s nearly always missing. If the straightforward version didn’t work, write down that it didn’t work, or the next person is going to try it, because it’s straightforward.
The constraint that isn’t visible anywhere in the code. The redirect that looks wrong because the client printed the old URL on a few thousand postcards. The delay that’s there because a payment provider’s sandbox behaves differently from the live one. Those are the lines that get tidied away by somebody being helpful, and the message is the only place they were ever written down.
And what you’re not sure about. “I think this is the wrong layer for it, I couldn’t find a way to do it in the right one without opening up checkout” is worth a lot to whoever reads it next, and it costs you nothing except a small amount of pride.
Read the cutoff time in the store's timezone, not the browser's
Customers on the East Coast were being told they had missed the
same-day cutoff when they hadn't. The cutoff is 5 pm Pacific and the
comparison was running against whatever the browser's clock said, so
anyone ordering between 5 and 8 in their own evening got tomorrow's
delivery date.
The obvious fix is to send the cutoff down as UTC and compare there,
but the warehouse edits it in the admin as a wall clock time and
nobody wants to explain daylight saving to them twice a year. This
converts at render time against the store's own timezone setting
instead.
That setting is optional and defaults to empty. If anyone clears it,
every order goes back to reading the browser and nothing anywhere
will tell us.
The last paragraph in that one is the part I’d argue for. It’s a known future problem, recorded in the one place that will still exist when the problem turns up, which is more than I can say for the ticket, the wiki, or my memory.
If applied, this commit will
Subject line in the imperative. “Add,” “Fix,” “Remove,” not “Added” or “Fixes.”
The reason isn’t taste. Git writes messages by itself in that voice, so a log where half the entries are past tense reads like two people taking turns at it. The test everybody quotes, from the post everybody links, is that the subject should finish the sentence “if applied, this commit will…,” which sounds like a rule somebody figured out backwards after the fact and is still the most useful thing to have in your head at 6 pm.
Then a blank line, because git treats the first paragraph as the subject and will happily turn the whole message into one very long subject line if you let it.
Three habits I dropped
Using the ticket link as the entire message. “See PROJ-4471.” That was fine right up until the day the tracker was replaced, and every project I’ve worked on has replaced its tracker at least once. Link to it, sure, but write enough that the message survives the link going dead.
Batching a day’s work into one commit. A commit that does four things can’t explain why, because there are four whys and they’re tangled together in one diff. It also can’t be undone in pieces, so the one part somebody wants to revert comes with three parts they wanted to keep. The selfish version of this argument is that a history built that way can’t be searched either.
Narrating the diff in prose. “Changed the timeout from 30 to 60.” I can see that. I could see it before I read your sentence.
Do as I say
I’d like to tell you I do all of this consistently. I don’t. My own log has plenty of “fix” in it, plenty of “tweak,” one “argh” that I turned up while writing this post, and a depressing number of one-line messages sitting on commits that touch nine files. What a tool.
What’s actually moved the ratio isn’t discipline, it’s picking a reader. Two years ago I wrote about reading the log before the code when you inherit something, and this is the same thing from the other end of it. I stopped writing the message to the project and started writing it to one person, and the person is whoever runs blame on a line I wrote and has to decide whether they’re allowed to delete it.
Most of the time that person is going to be me. Two years out, no memory of this client, no memory of why a redirect would ever need four conditions, holding a cleanup ticket and a strong opinion about nested conditions. She deserves better than three letters. She gets it maybe half the time now, which is not a great record but is an outright improvement on the woman who typed fix and went home.