A good commit message explains why a change was made, in language a teammate or your future self can understand months later. The diff already shows what changed line by line, so your message should not just restate it. Use a short imperative subject line of about 50 characters, leave a blank line, then write a body that gives the reason and any context a reviewer needs. Keep each commit to one logical change. That is the whole discipline, and it pays off every time you read a log or hunt for a regression.
Why commit messages matter
A commit message is documentation that lives next to the code forever. When a bug appears six months from now and you run git blame on the offending line, the message attached to that change is your first and sometimes only explanation. A message like fix tells you nothing; a message that says the change works around a race condition in a specific service saves you an hour of guessing.
Good messages also make code review faster, make reverts safe, and make tools like git bisect actually work. Bisect finds the commit that introduced a bug by checking out points in history; that only helps if each commit is a single coherent change rather than a grab bag.
The format that works
The widely used convention is simple. Write a concise subject in the imperative mood, then a body separated by a blank line.
Fix redirect loop on expired sessions
The session middleware refreshed the cookie after the auth check,
so an expired session bounced between login and dashboard. Move the
refresh before the check and add a regression test.
Refs: TICKET-482
Why imperative mood? A commit completes the sentence "If applied, this commit will ...". So you write Add retry logic, not Added or Adds. It reads oddly at first and becomes second nature fast.
Subject and body rules
| Rule |
Guideline |
Reason |
| Subject length |
Around 50 characters, hard cap near 72 |
Stays readable in logs and tools |
| Subject mood |
Imperative (Fix, Add, Remove) |
Matches git own generated messages |
| Subject case |
Capitalize, no trailing period |
Cleaner, consistent history |
| Blank line |
Always between subject and body |
Tools treat line one as the title |
| Body width |
Wrap around 72 characters |
Readable in terminals without scroll |
| Body content |
Explain why and any trade-offs |
The diff already shows the what |
Many teams add a lightweight prefix system called Conventional Commits, where messages start with a type such as feat:, fix:, or docs:. It is optional, but it enables automated changelogs and version bumps, so it is worth adopting on shared projects. If you are still building the muscle for clean changes, our guide on how to debug code faster pairs well, because small commits make bugs easier to isolate.
How to write one, step by step
- Stage one logical change. Use
git add -p to stage just the hunks that belong together. Resist bundling an unrelated cleanup into a feature commit.
- Write the subject as a command. Summarize the change in under 50 characters in the imperative.
- Add a body if the why is not obvious. Explain the motivation, the approach, and any trade-off you accepted. Skip the body only for truly trivial changes.
- Reference context. Link the ticket, issue, or discussion so the reasoning is one click away.
- Re-read before committing. A thirty-second proofread now beats confusion later.
Common mistakes
- Vague subjects.
update, fix stuff, and wip are noise. Say what and imply why.
- Giant commits. A 40-file commit titled
refactor cannot be reviewed or reverted cleanly. Split it.
- Restating the diff. "Changed line 14" adds nothing the diff does not already show.
- Skipping the body when it matters. If the reason is not obvious from the code, write it down.
- Inconsistent style across a team. Agree on a convention once and let a linter or template enforce it.
FAQ
How long should a commit message be?
Keep the subject around 50 characters and under 72. The body can be as long as needed to explain the why, wrapped near 72 characters per line. Trivial changes can skip the body entirely.
Should I use Conventional Commits?
On a shared or open-source project, yes, because it enables automated changelogs and semantic versioning. On a quick personal script, a plain clear message is enough.
Why imperative mood?
Git own automated messages use it, and a commit reads naturally as "If applied, this commit will Add X". Consistency with that convention keeps history uniform.
Can I rewrite a bad commit message?
Yes. Use git commit --amend for the most recent commit, or an interactive rebase for older ones, but avoid rewriting history that others have already pulled.
Where to go next
Debug code faster with a tighter loop, learn how to review code effectively, and understand what a pull request is.