Topic 7.2
Writing Commit Messages That Matter
In one line
A good commit message explains why a change was made, not just what changed — and the Conventional Commits format gives that structure a consistent, machine-readable shape that tooling can build on.
Think of it like this
A lab notebook entry that says 'changed the mixture' versus one that says 'reduced the acid concentration by 10% because the previous batch corroded the container within an hour' — the second one is genuinely useful to someone (including the same scientist) reading it months later; the first one tells you almost nothing beyond what a diff would already show.
Key ideas
- 01
The most widely-cited convention: a short SUBJECT line (under ~50 characters, written in the imperative mood — 'Add rate limiting' not 'Added rate limiting' or 'Adds rate limiting'), a blank line, then a longer BODY explaining the reasoning, context, and any trade-offs — not every commit needs a body, but genuinely non-obvious changes benefit enormously from one.
- 02
The single most valuable habit: explain WHY, not WHAT.
git log -porgit showalready displays exactly what changed, line by line — a commit message that just restates the diff in prose ('changed the loop condition') adds zero information a reader didn't already have; a message explaining WHY ('changed the loop condition to avoid an off-by-one error when the list is empty') adds the one thing the diff genuinely can't show on its own. - 03
CONVENTIONAL COMMITS is a widely-adopted format that prefixes the subject line with a TYPE:
feat:(a new feature),fix:(a bug fix),docs:(documentation only),refactor:(no behavior change),test:(adding or fixing tests),chore:(maintenance, tooling). This structure is genuinely machine-parseable, which is exactly why it's often enforced via acommit-msghook (Phase 4.4) and used to automatically generate changelogs. - 04
A
BREAKING CHANGE:footer (or a!right after the type, likefeat!:) in a Conventional Commits message flags a change that breaks backward compatibility — tooling built on this convention (like automated semantic-version bumping, Phase 6.3) uses exactly this marker to decide whether a release should bump the MAJOR version automatically. - 05
A commit message referencing a specific issue or ticket number (
Fixes #142, or a Jira ticket ID) creates a genuinely useful, permanent link between the code change and the reasoning/discussion that led to it — many platforms automatically turn these references into clickable links and even auto-close the referenced issue when the commit is merged.
Code & diagrams
Weak versus genuinely useful, side by side.
## Weak — restates the diff, explains nothing
"fix bug"
"updates"
"changed stuff in PaymentService"
## Genuinely useful — plain style
Prevent duplicate charge on payment retry
The payment gateway occasionally times out AFTER successfully charging
the customer, causing our retry logic to charge them a second time.
Added an idempotency key derived from the order ID so retried requests
are recognized and safely ignored by the gateway.
Fixes #142
## Genuinely useful — Conventional Commits style
fix(payment): prevent duplicate charge on retry
Add an idempotency key derived from the order ID so a retried request
after a gateway timeout is safely ignored instead of double-charging
the customer.
Fixes #142
## A breaking change, flagged explicitly
feat(api)!: require API key on all /v2 endpoints
BREAKING CHANGE: unauthenticated requests to /v2 now return 401.
Clients must migrate to include an Authorization header.Explain it without notes
Why is 'explain why, not what' the single most important rule for writing a genuinely useful commit message?
What makes the Conventional Commits format specifically useful to tooling, beyond just being a nice convention for humans to read?
Practice
Rewrite three of your own recent, real commit messages (or write three hypothetical ones for imagined changes) following the imperative-mood subject plus why-focused body structure.
Convert those same three messages into Conventional Commits format, choosing an appropriate type (feat/fix/docs/refactor/test/chore) for each.
Trade-offs
- ↔
Writing genuinely thoughtful commit messages takes real, if small, extra time on every single commit — for a tiny, obvious, low-stakes change, an equally short and obvious message is entirely appropriate; the discipline matters most exactly where it's most tempting to skip it, on the commits with real, non-obvious reasoning behind them that would otherwise be lost forever the moment the author forgets it.
Done when you can
I can write a commit message with an imperative-mood subject and a why-focused body.
I understand the Conventional Commits format and can pick an appropriate type for a given change.
I know why 'explain why, not what' is the single most valuable commit-message habit.