Eighteen months ago, I was debugging a production issue at 2am. The service was returning 500 errors intermittently, the logs were unhelpful, and I was running out of ideas. Then I found a commit from eight months earlier with the message: "fix: handle null user_id in payment processor — this will cause silent failures if user_id is None, added explicit check and error logging." The commit was from me. I had no memory of writing it.
The fix took me about ten minutes once I found that commit. Without it, I would have been debugging for hours. The commit message was the documentation. The commit message was the thing that saved the night.
I've been thinking about commit messages ever since. Not as a best practice to follow — as a form of communication with a specific audience: the person who will be debugging this code at 2am, six months or a year from now, who may or may not be you, who is tired and stressed and needs to understand what happened here and why.
Most commit messages are written for the person making the commit, in the moment of making it. "fix bug," "update," "WIP," "changes." These messages are useless to the 2am debugger. They tell you that something changed, not what changed or why. The diff tells you what changed. The commit message should tell you why.
The format I've settled on: a short summary line (under 72 characters), then a blank line, then a paragraph or two explaining the context. What was the problem? What was the root cause? What alternatives did you consider? What are the edge cases? What should the next person know before they touch this code?
The systems thinking that makes this work is treating the commit history as a document. Not a log of changes — a document that explains the evolution of the system. When you read the commit history of a well-maintained codebase, you can understand not just what the code does but why it does it that way. The decisions are visible. The context is preserved.
This requires a shift in how you think about commits. A commit is not just a save point. It's a communication. You're communicating with your future self, with your teammates, with whoever inherits this codebase. The commit message is the letter you're leaving them. Write it accordingly.
The dev tooling ecosystem has been moving toward better commit message tooling — conventional commits, commitizen, semantic versioning tied to commit types. These are useful, but they're not sufficient. A commit message that follows the conventional commits format but doesn't explain the why is still a bad commit message. The format is the container. The content is what matters.
I've started reviewing commit messages in code review. Not just the code — the message. If the message doesn't explain why the change was made, I ask for a revision. This is controversial. Some people think commit messages are not worth the time. Those people have not spent enough time debugging at 2am.
The fringe tech community has been experimenting with AI-generated commit messages. The results are mixed. AI is good at summarizing what changed — it can read the diff and produce a reasonable summary. It's not good at explaining why, because why requires context that isn't in the diff. The why is in your head. You have to put it in the message.
The commit I found at 2am was not a great commit message by any formal standard. It was a single sentence. But it contained the essential information: what the problem was, what the fix was, and what would happen if the fix wasn't there. That's all a commit message needs to do. Tell the 2am debugger what they need to know.
The dev culture that I want to be part of is one that treats documentation as part of the work, not as an afterthought. The commit message is the smallest unit of documentation. If you can't write a good commit message, you probably don't understand the change well enough to be making it. That's a useful test. Apply it.