Write good Git commit messages | Christian Rackerseder
Christian Rackerseder<br>Write good Git commit messages
Published 2026-05-16<br>in Git · 12 min read<br>Permalink Git history is part of the system #
Most teams treat commit messages as a small detail.
Something you write quickly before pushing.<br>Something you clean up before a pull request.<br>Something that mostly exists because Git requires a message.
I think that is the wrong way to look at it.
A commit message is not only a label for a diff. It is part of the engineering history of the system.
It explains what changed. Ideally, it also hints at why the change exists.
That matters later.
Not during the happy path where everyone remembers the context. It matters when something breaks, when a release has to be reverted, when a regression appears, or when someone tries to understand why a strange edge case exists.
At that point, the commit message is no longer decoration.
It is debugging infrastructure.
Good commit messages support future debugging #
I already wrote about why git bisect is one of Git’s most underrated features.
git bisect helps you find the exact change that introduced a behavior.
But finding the commit is only half of the story.
After Git tells you which commit introduced the regression, you still need to understand what that commit was supposed to do.
A message like this does not help much:
fix stuff<br>Neither does this:
changes<br>Or this:
WIP<br>Those messages may have felt harmless when they were written. But later, during debugging, they are useless.
They force the next person to reconstruct intent from the diff alone.
Sometimes that next person is another engineer.<br>Sometimes it is you, three months later, with no memory of the original context.
That is why commit messages matter.
They reduce archaeology.
Use the imperative mood #
A good default is to write the commit subject in the imperative mood.
That means the message reads like an instruction:
Add retry handling for failed uploads<br>Not:
Added retry handling for failed uploads<br>And not:
Adds retry handling for failed uploads<br>The common heuristic is simple:
If applied, this commit will ...<br>So this works:
If applied, this commit will add retry handling for failed uploads<br>This does not:
If applied, this commit will added retry handling for failed uploads<br>And this also does not:
If applied, this commit will adds retry handling for failed uploads<br>That may sound like grammar nitpicking.
It is not.
The imperative mood frames each commit as an operation on the codebase. It describes what applying the commit does.
That is exactly how Git history behaves.
A commit is not a diary entry. It is not primarily about what the author did yesterday.
It is a change that can be applied, reverted, cherry-picked, inspected and bisected.
The message should fit that model.
The subject should say what changes #
A commit subject should be specific enough to be useful in a log.
This is weak:
Fix bug<br>This is better:
Fix missing reconnect after token refresh<br>This is weak:
Refactor code<br>This is better:
Extract message retry state machine<br>This is weak:
Update tests<br>This is better:
Add regression test for reconnect timeout<br>The better versions are not much longer. But they carry much more information.
They tell the reader what area changed. They give the log a shape. They make scanning history easier.
That is the point.
A good commit message does not need to explain every line of the diff. The diff already shows that.
The subject should summarize the meaningful change.
The body should explain why when the why matters #
Not every commit needs a long body.
Some changes are obvious enough:
Fix typo in settings label<br>There is no need to write a novel for that.
But some changes do need context.
Especially when the diff does not explain the decision by itself.
For example:
Disable optimistic reconnect after auth failure
The reconnect loop kept retrying with an invalid token after the<br>session had already expired. That made the client appear connected for<br>a short time while every request failed.
Force the auth flow to refresh the session before reconnecting again.<br>That body is useful because it explains intent.
It says what problem existed. It says why the new behavior is safer. It gives future readers a starting point.
This becomes valuable when someone later asks:
Why do we do it this way?<br>Why did we not simply retry again?<br>Can this be reverted?<br>Is this related to the incident from last month?
A good commit body can answer those questions without searching Slack, Jira, pull request comments or someone’s memory.
Do not make the message repeat the diff #
A bad commit message often describes the mechanics only:
Change timeout from 5000 to 10000<br>That may be technically true. But it does not explain much.
The diff already shows that a number changed.
The more useful message is about intent:
Increase reconnect timeout for slow mobile networks<br>Now the reader knows why the...