Skip to content

The Art of Git Commit Messages: Writing History That Future Developers Will Thank You For

Why I spend more time on commit messages than most people spend on comments, and how it pays dividends months later.


Reading My Own History Was Painful

I was debugging a production issue and ran git log to understand why a particular change was made six months ago. The commit message: "fix bug." That was it. No context, no reasoning, no reference to what bug. I checked blame on surrounding lines: "update code," "minor changes," "WIP." I had to read every diff, reconstruct the intent, and piece together what the developer was thinking. That developer was me. That was the day I decided to treat commit messages as a first-class communication channel.

The Anatomy of a Great Commit Message

After years of refining my approach, I follow a strict structure:

<type>(<scope>): <subject>

<body>

<footer>

The subject line (max 50 characters) answers: "what does this commit do?" Written in imperative mood — "add feature," not "added feature" or "adds feature."

The body (wrapped at 72 characters) answers: "why was this change necessary? what approach was taken? what alternatives were considered?"

The footer contains references: issue numbers, breaking change notes, co-author credits.

Real Examples From My Work

Bad (my old style):

fix timeout issue

Good (my current style):

fix(auth): increase token refresh timeout to 30s

The default 5s timeout caused failures on high-latency
connections, particularly for users in Southeast Asia.
Monitoring showed 12% of refresh attempts timing out
since the v2.3 release.

30s matches the upstream OAuth2 provider's maximum
response time under load. Considered per-region timeouts
but deferred — the uniform increase resolves 98% of
failures with less complexity.

Fixes: PROJ-4521
Monitoring: https://grafana.internal/d/auth-timeouts

Six months from now, any developer can understand: what changed, why, what data supported the decision, and what alternatives were considered but rejected.

The Seven Rules I Follow

  1. Separate subject from body with a blank line. Tools rely on this — git log --oneline shows only the subject, email formatters use it as the email subject.

  2. Limit the subject to 50 characters. Forces precision. If you can't describe the commit in 50 characters, the commit is probably too large.

  3. Capitalize the subject line. "Add user validation" not "add user validation."

  4. No period at the end of the subject. Space is precious at 50 characters.

  5. Use imperative mood in the subject. "Fix bug" not "Fixed bug." Think of it as completing the sentence: "If applied, this commit will fix bug."

  6. Wrap body at 72 characters. Git doesn't wrap automatically. Terminal tools assume 72-character width for diffs and logs.

  7. Explain what and why, not how. The diff shows how. The message explains why.

Conventional Commits: Structure That Scales

I use Conventional Commits format for the subject line:

feat(scope): add new capability
fix(scope): correct broken behavior
docs(scope): update documentation only
refactor(scope): restructure without behavior change
test(scope): add or fix tests
chore(scope): maintenance, dependencies, tooling
perf(scope): improve performance
ci(scope): CI/CD pipeline changes

This structure enables automation: - Changelogs generated from commit history - Semantic version bumping based on commit types - Filtering history by type: git log --grep="^feat"

The Body: Where Real Value Lives

The subject line gets people to the commit. The body makes them understand it. I include:

Context: What situation prompted this change?

Users reported intermittent 403 errors during peak hours.
Investigation showed the session cache was evicting entries
under memory pressure, causing valid sessions to require
re-authentication.

Decision rationale: Why this approach over alternatives?

Considered three approaches:
1. Increase cache size (quick but doesn't solve the root cause)
2. Switch to Redis-backed sessions (correct but large scope)
3. Add cache-miss fallback to database (targeted, minimal risk)

Chose option 3 as it resolves the immediate issue while
option 2 is planned for Q3.

Impact: What should readers know about the effects?

This adds a database query on cache miss (~5ms p99).
Under normal load, cache hit rate is 99.7%, so impact
is negligible. Under the failure scenario, users get
a 5ms delay instead of a 403 error.

Commit Messages as Documentation

I treat commits as the most honest documentation. Unlike README files that go stale, commit messages are permanently tied to the exact code change they describe. They are:

  • Discoverable via git blame — click any line to understand its history
  • Permanent — unlike comments that get deleted during refactoring
  • Contextual — they explain the transition, not just the current state
# Why was this line written this way?
git blame src/auth/session.py

# What was the developer thinking?
git log --format="%H %s" -- src/auth/session.py

# Full context for a specific change
git show <commit-sha>

Commit Messages for Reverts

When reverting, I explain why the original was wrong:

revert: "feat(cache): add LRU eviction policy"

This reverts commit a1b2c3d.

The LRU eviction was causing session loss under high load.
The eviction threshold (100MB) was too aggressive for our
workload pattern — users with long sessions (>30min) were
losing state.

Reverting while we implement size-based eviction with
session-duration weighting (PROJ-4890).

Teaching the Team

Commit message quality is cultural. I established it on my team through:

  1. commit-msg hook that enforces format (length, conventional prefix)
  2. PR template that asks "do your commit messages explain the why?"
  3. Leading by example — detailed messages on every commit, especially boring ones
  4. Code review feedback — I comment on unhelpful commit messages, not just code

Key Takeaway

A commit message is a letter to a future developer. It costs two minutes to write well and saves hours of archaeology later. The diff shows what changed. The message explains why it matters. Together, they form a complete historical record that no amount of documentation can replicate. Write as if the next reader has never seen this code before — because in six months, that reader is you.


Tags: git, commit-messages, documentation, conventions, communication

Reference