Commit Message Craft
Commit Message Craft
Core Concepts
1. The 50/72 rule (Tim Pope, 2008)
Subject line: hard limit 50 characters. Body: wrap at 72.
Mechanical rules:
- Subject line ≤ 50 chars. Hard ceiling is 72, but cross 50 only when truly unavoidable.
- Blank line between subject and body — required.
- Body wrap at 72.
- No trailing period on the subject.
2. Imperative mood for the subject
Write the subject as a command the commit gives the codebase, not as a past-tense report.
Test: prepend “If applied, this commit will ___”. The result must be a grammatical English sentence.
Add retry logic to S3 uploader— passesAdded retry logic to S3 uploader— fails
3. Conventional Commits: structure beyond the basics
<type>[optional scope][!]: <description>
[optional body]
[optional footer(s)]
Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
4. Breaking changes: ! and BREAKING CHANGE: footer
Method 1 — exclamation mark in the header:
feat(api)!: remove deprecated /v1/users endpoint
Method 2 — footer token:
feat(api): drop XML response format
BREAKING CHANGE: Clients sending Accept: application/xml now receive
406 Not Acceptable.
5. The “why not what” rule for the body
The diff already shows what changed. The body must explain why.
Body content checklist:
- What problem did this commit solve?
- Why this approach and not an alternative?
- Any non-obvious side effect or trade-off?
- Links to tickets, design docs, or incident reports.
Body anti-content:
- Don’t restate the subject line.
- Don’t narrate the diff line by line.
6. Multi-commit storytelling for PR reviewers
- Prep commits first — refactors, renames, type-only changes with no behavior change.
- The substantive commit — the one that does the actual feature/fix.
- Test commit(s) — if tests are separated.
- Cleanup commits last — docs, changelog, version bumps.
7. Fixup / squash / autosquash discipline
git commit --fixup=<sha> writes a commit message starting with fixup! <original-subject>.
--fixupdiscards the fixup’s commit message; only the original is kept. Use for typo fixes.--squashkeeps both messages joined. Use when the “fix” adds meaningful nuance.
Never push fixup commits to a shared branch as-is.
8. Signed-off-by and the Developer Certificate of Origin
fix(net): handle ECONNRESET during initial TLS handshake
The current code path treats a reset before the ServerHello as a
generic IO error, masking the actual TLS issue.
Signed-off-by: Mitchell Hudson <[email protected]>
Use git commit -s (or --signoff) to add automatically.
9. Other trailers
Co-authored-by: Name <email>— GitHub credits both authorsCloses #123,Fixes #123,Resolves #123— GitHub auto-closes the issue
Templates
Template — Conventional Commit with body and footers
<type>(<scope>): <imperative description>
<Why this change exists. What problem it solves or what behavior it
enables. Wrap at 72 columns.>
Closes #1234
Co-authored-by: Pat Reviewer <[email protected]>
Signed-off-by: Mitchell Hudson <[email protected]>
Anti-Patterns
- “Fixed bug” / “Updates” / “WIP” — useless.
- Past-tense subjects —
Added,Fixed,Refactored. - Restating the diff in the body.
- Stacking unrelated changes in one commit.
- Burying breaking changes without
!orBREAKING CHANGE:.