Everyone has an opinion about Git commit messages.
After years of working on Rails applications, these are the conventions I’ve settled on. They’re simple, easy to remember, and optimize for the one thing I care about most: making the project history pleasant to read months or years later.
The 50/72 Rule
Every commit message follows the same structure:
- 50-character limit on the first line (the subject)
- Blank line
- 72-column limit for the body (if one is needed)
Use allowlist for marketing pages dynamic render
The allowlist prevents Rails from rendering dynamic pages
that should remain cacheable while preserving existing
behavior for authenticated users.
The 50-character subject is short enough to display cleanly in tools
like git log --oneline, GitHub, GitLab, and terminal interfaces.
The 72-column body isn’t about old terminals. It’s about readability. Wrapped text is simply easier to read, especially when viewing commits in a terminal or side-by-side diff.
Use the Imperative Mood
Subjects should be written as commands.
Use allowlist for marketing pages dynamic render
Add Sqid to UDA
Store date fields as epoch seconds
Not:
Used allowlist for marketing pages dynamic render
Added Sqid to UDA
Stored date fields as epoch seconds
Why?
Because it matches Git’s own voice.
Git says:
Merge branch 'feature-abc'
not
Merged branch 'feature-abc'
More importantly, the imperative describes the intent of the change.
A useful way to think about it is:
If applied, this commit will…
Examples:
- If applied, this commit will fix broken list links.
- If applied, this commit will add a relative date filter.
That mental model makes commit messages surprisingly easy to write.
Be Specific
The subject should tell the reader exactly what changed.
Good
Fix broken list links in search results
Store date fields as epoch seconds
Add relative date filter to lists
Bad
Fix links
Change format
Add filter
Bugfixes
The bad examples force the reader to open the commit just to discover what happened.
A good subject should answer those questions immediately.
- Which links?
- What format?
- Which filter?
- Which bug?
Every extra word should add information.
The Body Explains Why, Not What
Most commits don’t need a body.
The diff already shows what changed.
Only add a body when future readers would benefit from understanding why the change exists.
For example:
TaskChampion stores dates as epoch-second strings. When
Taskwarrior parsed the leading year digits as epoch values,
tasks appeared as 1970-01-01. Fields written through typed
setters were unaffected because taskchampion-rb converts
those values internally.
A bad body simply narrates the diff:
Changed task.rb.
Updated parser.
Added tests.
Renamed a variable.
Anyone can read the diff to learn that. The body should preserve knowledge that the code itself cannot.
No AI Attribution
I don’t allow lines like:
Generated by ChatGPT
Co-authored-by: Claude
The commit history should describe the evolution of the software, not the tools used to edit it.
The interesting part is why the code exists, not whether an LLM happened to help create it.
No Conventional Commits
I don’t use Conventional Commits.
feat:
fix:
chore:
docs:
refactor:
I understand why they exist, especially for large open source projects with automated release tooling.
For the projects I work on, both professionally and personally, they add more ceremony than value.
The subject already tells me what happened.
Add relative date filter to lists
doesn’t become clearer by writing
feat: add relative date filter to lists
The prefix is redundant.
Changelogs Should Be Written for Humans
One of the biggest arguments for Conventional Commits is automatic changelog generation.
I think that’s backwards.
A changelog is a curated document that explains what matters to users.
A commit log is a chronological engineering history.
They’re different documents serving different audiences.
Trying to generate one from the other encourages writing commit messages for tooling instead of for humans reading Git history.
I’d rather write commit messages that make git log enjoyable to browse
and write release notes separately when they’re actually needed.
That’s It
My rules are intentionally minimal:
- Follow the 50/72 rule.
- Write the subject in the imperative mood.
- Be specific.
- Explain why, not what, when a body is needed.
- Skip AI attributions.
- Skip Conventional Commits.
The goal is not to satisfy a linter.
The goal is to leave behind a project history that another developer, including future me, can read quickly and understand with confidence.