How to Write Commit Messages Your Team Can Read
A good commit message has a subject line under about 50 characters that completes the sentence “this commit will,” and a body that explains why the change was needed. The diff already shows what changed. The commit message exists to record the reasoning, which is the only part that cannot be recovered later.
That distinction is the whole discipline. Almost every bad commit message is bad because it describes the diff.
The Subject Line
Imperative mood, present tense, no trailing full stop. “Fix race condition in session refresh,” not “Fixed” and not “Fixes.”
The reason is that git itself generates messages in the imperative: “Merge branch,” “Revert commit.” Matching that keeps the log consistent, and consistency is what makes a log scannable.
Keep it under 50 characters so it is not truncated in git log --oneline, GitHub lists, and blame views. If you cannot describe the change in 50 characters, you are usually committing two changes.
Be specific. Compare “Fix bug” against “Fix session token expiry off-by-one.” Only one of those is useful when you are bisecting a regression at 11pm.
The Body Explains Why
Wrap at 72 characters, leave a blank line after the subject, and answer three questions: what was wrong, why this fix, and what else it affects.
Fix session token expiry off-by-one
Tokens were being treated as valid for one refresh cycle past their stated expiry because the comparison used >= against the stored timestamp rather than >. Users on slow connections could authenticate with a token the server had already logged as expired, which produced the mismatched audit entries in INC-220.
Chose to tighten the comparison rather than extend the stored expiry, because the audit log is the source of truth for compliance reporting and it must agree with the auth decision.
Sessions now expire up to 30 seconds earlier in practice. Acceptable given the refresh window is 15 minutes.
That message will still be useful in three years. It records a bug, a decision, the alternative rejected, and a consequence someone might otherwise report as a new bug.
Not every commit needs a body. A typo fix does not. Anything involving a decision does.
Do Not Narrate the Diff
The most common failure mode.
Before:
Updated UserService.ts and added a new method to the auth handler, also changed the config file and updated some tests
After:
Add refresh-token rotation to prevent replay attacks
Refresh tokens were single-use in policy but not enforced in code, so a captured token could be reused indefinitely. Rotation invalidates the previous token on each refresh.
The first version lists files, which git already knows. The second explains a security decision, which git does not.
Reference, Do Not Depend
Include the ticket reference. Do not make the ticket the only explanation.
“Fixes JIRA-4471” is useless when the tracker has been migrated twice and the old instance is gone. Put one sentence of actual reason in the message, then reference the ticket for detail.
Commit Granularity
Message quality and commit granularity are the same problem. If a message needs the word “and,” the commit probably needs splitting.
One logical change per commit. A refactor and a behavior change in the same commit make review harder and make reverting risky, because you cannot undo one without the other.
The practical version: commit messily while working, then tidy the history before opening a pull request. Interactive rebase exists for this.
Conventional Commits, If You Use Them
Many teams prefix with a type: feat:, fix:, chore:, docs:. This enables automated changelogs and semantic versioning.
It works well if the team applies it consistently and if the tooling actually consumes it. If half the team uses prefixes and nobody generates a changelog from them, the prefix is decoration. Decide as a team, then be consistent.
Cleaning Up Before You Push
A Wrivio Context for this could say:
Rewrite this as a git commit message. Subject line in imperative mood, under fifty characters, no trailing full stop. Blank line, then a body wrapped at seventy-two characters explaining what was wrong, why this approach, and any consequence. Do not list changed files or describe the diff. Neutral technical register. Keep every ticket reference, file path, and figure exactly as written and do not invent reasons or effects that are not in my notes.
Press Ctrl+Shift+Space over your terminal or editor, paste the rough note you made while fixing it, and run it. The constraint against inventing reasons is the one that matters: a plausible-sounding rationale that was not yours becomes permanent, misleading history, and someone will act on it.
What Makes It Worth Doing
The audience is not your reviewer. It is you in eighteen months, running git blame on a line that looks wrong, trying to work out whether it was deliberate.
A log full of “fix stuff” and “wip” means every one of those investigations starts from nothing. A log that records reasoning means most of them end in thirty seconds.
Common Questions
Does anyone actually read commit messages?
Not routinely. They are read at exactly the moments when it matters most: during incidents, bisects, and blame investigations.
What about squash merges?
The squashed message becomes the permanent record, so it deserves the most care. Individual commits on a branch can be scrappier.
Should AI write commit messages?
For phrasing and formatting, fine. Tools that generate messages from the diff alone produce exactly the diff-narrating messages you are trying to avoid, because the reason is not in the diff.
Download Wrivio for Windows to turn a scratch note into a properly formatted commit message without leaving the terminal.
Read Next
How to Write a Pull Request Description
Reviewers need to know what changed, why, and what you want them to look at. A structure that gets faster, better reviews on the same code.
How to Write a Bug Report Developers Can Act On
Steps, expected, actual, environment. Why most bug reports get bounced back, and how to write one that gets fixed instead of triaged into a question.
How to Write a README Introduction
The first three lines decide whether someone keeps reading or closes the tab. What belongs at the top of a README, and what belongs much further down.
How to Write in a Group Chat Without Starting a Fight
Group chats amplify tone and strip context. The specific habits that prevent a normal message from becoming a three-day argument.
This article is filed underProductivity & Operations, which has 49 articles.