Commit Messages

Commit Messages as Documentation

Commit messages serve as the primary narrative documentation of repository evolution. Well-crafted messages enable efficient debugging, code archaeology, and team coordination, while poor messages create friction in every subsequent interaction with repository history.

The Newsfeed Mental Model

Think of your commit log as a newsfeed for your project, where each commit message is a headline. Just as you can skim newspaper headlines and understand what’s happening in the world, developers should be able to scan git log --oneline and understand project evolution without reading code.

Good Headlines:

  • Tell you enough to know what the story is about
  • Don’t have to tell the whole story
  • Fit in limited display space

Practical Reality: Commits that are perfect, single units of work, wrapped up in perfectly worded messages, are the exception rather than the rule. Don’t beat yourself up for a messy commit with a vague label like “fixed the header”—just know that better is possible and aim for it when you can.


The Cost of Poor Commit Messages

Common Anti-Patterns:

git log --oneline
abc1234 fix
def5678 updates
ghi9012 stuff
jkl3456 wip
mno7890 final
pqr1111 final final

Consequences:

  • git bisect sessions require code inspection instead of message scanning
  • git blame provides no context for why changes were made
  • Code review becomes archaeological expedition rather than focused examination
  • Team members duplicate investigation effort

Contrast: Professional Message Quality:

abc1234 Fix authentication timeout on slow networks
def5678 Refactor user model to support OAuth integration
ghi9012 Add rate limiting to API endpoints (fixes #243)
jkl3456 Optimize database queries in dashboard (30% improvement)

Value: Clear messages enable rapid understanding without code examination.


The Conventional Commits Standard

Structure Template

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

<body>

<footer>

Component Breakdown:

Type (required): Categorizes change intent

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation only
  • style: Code style (formatting, no logic change)
  • refactor: Code restructuring (no behavior change)
  • perf: Performance improvement
  • test: Test addition or correction
  • chore: Build process, tooling, dependencies

Scope (optional): Component or module affected

  • (auth): Authentication system
  • (api): API layer
  • (ui): User interface
  • (db): Database layer

Subject (required): Imperative mood, concise summary (≤50 chars)

Body (optional): Detailed explanation (wrapped at 72 chars)

Footer (optional): Breaking changes, issue references


Example: Well-Structured Commit

feat(auth): implement JWT token refresh mechanism

Add automatic token refresh 5 minutes before expiration to prevent
session interruptions during active usage. Previous implementation
required users to re-authenticate when tokens expired mid-session.

Implementation:
- Background refresh timer in auth service
- Token expiration monitoring
- Graceful fallback to re-auth if refresh fails

Closes #156
BREAKING CHANGE: Auth configuration requires new `refreshWindow` parameter

Subject Line Crafting

The 50-Character Constraint

Rationale: Git tooling truncates at 50 characters in various displays:

  • git log --oneline output
  • GitHub/GitLab commit lists
  • Email subject lines (git send-email)

Pattern: <type>(<scope>): <imperative-verb> <what>

Effective Subject Lines:

✅ feat(api): add pagination to user list endpoint
✅ fix(auth): resolve race condition in token validation
✅ refactor(db): extract query builder to separate module
✅ perf(render): optimize component re-render logic

Ineffective Subject Lines:

❌ updated files
❌ fix bug
❌ changes
❌ WIP
❌ final version

Imperative Mood Usage

Rule: Write as if completing sentence “If applied, this commit will _

Correct:

  • “Add user authentication”
  • “Fix memory leak in cache module”
  • “Refactor payment processing pipeline”

Incorrect:

  • “Added user authentication”
  • “Fixing memory leak”
  • “Refactored code”

Technical Justification: Matches Git’s own message style and creates consistent imperative instruction set.


Body Composition

When to Include Body

Include Body When:

  • Change isn’t self-explanatory from subject
  • Implementation requires specific approach explanation
  • Alternative approaches were considered
  • Breaking changes introduced
  • Complex business logic implemented
  • Performance characteristics changed

Omit Body When:

  • Change is trivial (typo fix, whitespace)
  • Subject line fully explains change
  • Commit follows obvious implementation

Body Content Structure

Effective Body Pattern:

1. What: Describe the change
2. Why: Explain motivation and context
3. How: Detail implementation approach (if non-obvious)
4. Consequences: Note any side effects or considerations

Example:

refactor(cache): migrate from Redis to Memcached

Previous Redis implementation experienced memory pressure under high load,
leading to eviction of actively-used keys. Profiling revealed our usage
pattern (high read/write ratio, simple key-value storage, no persistence
requirements) better suited to Memcached's LRU algorithm.

Implementation:
- Updated cache interface to abstract storage backend
- Migrated connection pooling logic
- Adjusted TTL settings to match Memcached behavior

Performance impact:
- 40% reduction in memory usage
- 15% improvement in cache hit rate
- Simplified deployment (removed Redis persistence configuration)

Testing notes:
- Backward compatibility maintained through cache interface
- Load testing conducted with production traffic profile
- Gradual rollout planned with canary deployment

Refs: PERF-234

Footer Section: Metadata and References

Issue Tracking Integration

GitHub/GitLab Syntax:

# Close single issue
Closes #123

# Close multiple issues
Closes #123, #456

# Reference without closing
Refs #789
See #456

# Fix issue
Fixes #123
Resolves #456

Jira Integration:

JIRA-1234
PROJ-5678

Breaking Changes Declaration

Format:

BREAKING CHANGE: <description of breaking change>

# Or deprecated syntax
BREAKING CHANGE: API endpoint /users now requires authentication token

Example in Full Commit:

feat(api): add authentication to user endpoints

All user-related endpoints now require JWT authentication. This prevents
unauthorized access to user data and aligns with security requirements
from audit.

Migration guide:
1. Update API clients to include Authorization header
2. Refresh tokens implement 15-minute expiration
3. Update integration tests with authentication fixtures

BREAKING CHANGE: /api/users endpoints now return 401 for unauthenticated requests

Writing Commit Messages in Your Editor

Editor Integration for Better Messages

While the -m flag is convenient, Git integrates with text editors for composing multi-line commit messages with greater ease.

Configure Your Editor:

# VS Code
git config --global core.editor "code --wait"

# Sublime Text
git config --global core.editor "subl --wait"

# Vim
git config --global core.editor "vim"

# Emacs
git config --global core.editor "emacs"

# Nano
git config --global core.editor "nano"

Usage:

# Omit -m to use editor
git commit

# Git opens configured editor with template
# Write message, save, close
# Git automatically captures the message

Benefits:

  • Multi-line composition with proper formatting
  • Spell-checking and syntax highlighting
  • Access to commit message templates
  • Visual confirmation of line length constraints

Pro Tip: Editors like VS Code display column indicators, making it easy to respect the 50/72 character guidelines.


Commit Message Templates

Creating Template File

# Create template file
cat > ~/.gitmessage << 'EOF'
<type>(<scope>): <subject>

# Why (motivation and context):
#
# How (implementation approach):
#
# Consequences (side effects):
#

# Issue references:
# Closes #

# ────────────────────────────────────────────────────────────
# Types: feat, fix, docs, style, refactor, perf, test, chore
# Subject: Imperative mood, ≤50 chars, no period
# Body: Wrap at 72 chars, explain what and why vs. how
# Footer: Issue references, breaking changes
# ────────────────────────────────────────────────────────────
EOF

# Configure Git to use template
git config --global commit.template ~/.gitmessage

Usage: Running git commit (without -m) opens editor with template.


Template for Different Commit Types

Feature Addition Template:

feat(<scope>): <brief feature description>

# New feature details:
# - What: Feature implemented
# - Why: Business/user need addressed
# - How: Implementation approach
# - Testing: How feature was validated

Closes #

Bug Fix Template:

fix(<scope>): <brief fix description>

# Bug details:
# - Symptom: Observable behavior
# - Root cause: Technical cause
# - Solution: Fix approach
# - Prevention: How to avoid recurrence

Fixes #

Configuration for Team Consistency

Commit Message Linting

Install commitlint:

npm install --save-dev @commitlint/cli @commitlint/config-conventional

# Configure
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js

# Add to Git hooks
npm install --save-dev husky
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'

Enforcement: Invalid commit messages rejected at commit time.


Pre-Commit Hook for Message Validation

#!/bin/sh
# .git/hooks/commit-msg

commit_msg=$(cat "$1")
commit_regex='^(feat|fix|docs|style|refactor|perf|test|chore)(\(.+\))?: .{1,50}'

if ! echo "$commit_msg" | grep -qE "$commit_regex"; then
    echo "ERROR: Commit message does not follow Conventional Commits format"
    echo "Format: <type>(<scope>): <subject>"
    echo "Example: feat(auth): add JWT token refresh"
    exit 1
fi

Historical Analysis with Well-Structured Messages

Leveraging Message Quality

Search by Type:

# Find all features added
git log --grep="^feat"

# Find all bug fixes
git log --grep="^fix"

# Find database-related changes
git log --grep="(db)"

Generate Changelog:

# Features and fixes between versions
git log v1.0.0..v2.0.0 --grep="^feat\|^fix" --oneline

Identify Breaking Changes:

git log --grep="BREAKING CHANGE"

Advanced Message Techniques

Co-Author Attribution

Format:

feat(api): implement GraphQL endpoint

Co-authored-by: Jane Developer <[email protected]>
Co-authored-by: John Smith <[email protected]>

GitHub Recognition: Properly formatted co-author lines credit contributors in GitHub UI.


Anti-Pattern: Combining Unrelated Changes

❌ git commit -m "fix login bug and refactor database queries and update docs"

Correction: Separate commits

✅ git commit -m "fix(auth): resolve login failure on password reset"
✅ git commit -m "refactor(db): optimize user query performance"
✅ git commit -m "docs: update authentication flow documentation"

Integration with Development Workflow

Commit Message in Pull Requests

Strategy: PR description synthesizes commit messages

Example PR Description:

## Summary

Implements JWT-based authentication system

## Changes

- feat(auth): add JWT token generation
- feat(auth): implement token validation middleware
- test(auth): add authentication flow tests
- docs(auth): update API authentication documentation

## Breaking Changes

- API clients must now include Authorization header
- Legacy session-based auth deprecated

## Testing

- Unit tests: 45 new tests
- Integration tests: Authentication flow validated
- Load testing: 10K concurrent authenticated requests

Closes #123, #145

Amending Commit Messages

Scenario: Committed with poor message, want to improve before pushing.

# Amend most recent commit message
git commit --amend

# Amend without changing staged content
git commit --amend --only

Interactive Rebase for Older Commits:

git rebase -i HEAD~3

# In editor, change 'pick' to 'reword' for commits to modify
reword abc1234
pick def5678
pick ghi9012

# Git will prompt for new message for each reword

Further Reading

For deeper exploration of commit message philosophy and additional best practices, the Git community has produced excellent resources:

  • Tim Pope’s “A Note About Git Commit Messages”: Widely-cited guidelines that influenced modern commit message standards
  • Chris Beams’s “How To Write A Git Commit Message”: Comprehensive guide with the famous “Seven Rules”

These resources complement the Conventional Commits standard and provide historical context for commit message best practices.


Summary: Message Excellence as Force Multiplier

Core Principles:

  1. Clarity: Messages should explain what and why without code inspection
  2. Consistency: Follow team conventions (Conventional Commits recommended)
  3. Conciseness: Subject ≤50 chars, body wrapped at 72 chars
  4. Context: Include issue references, breaking changes, rationale

Practical Implementation:

  • Use commit templates for consistency
  • Configure editor integration for multi-line messages
  • Enforce with pre-commit hooks and linting
  • Review commit history quality during code review
  • Treat commit messages as documentation, not afterthought

Strategic Value: High-quality commit messages compound over time—each good message saves future debugging time, improves code archaeology, and enables automated changelog generation.

Technical Excellence: Commit message discipline transforms repository history from opaque sequence of changes into navigable, searchable project narrative.


Next Steps: Create Custom Git Commands with Aliases