As an experienced full-stack developer well-versed in Git, writing clear commit messages is one of the most indispensable skills in my toolkit. Commit messages serve as an essential thread to trace project histories, communicate context to other developers, and coordinate team collaboration.
In this comprehensive guide, I‘ll share the fundamental principles I follow to write commit messages that neatly summarize changes, highlight relevant details, and maintain a coherent changelog.
The Critical Importance of Well-Formatted Commit Messages
Clean commit logs act as:
- Archival records detailing when and why key changes occurred
- Release notes allowing project owners to ship versions with confidence
- Debugging tools that help identify when issues first emerged
- Onboarding guides bringing new developers up to speed
A survey from 2017 found 86% of developers feel a well-formatted git history improves overall code maintainability. 72% feel confident reverting changes from concise commit messages without breaking projects.
Unfortunately, a rushed or sloppy commit history hampers all these critical functions:
Hard-to-interpret commit messages create maintenance headaches (Source: Unspash)
In my experience collaborating on large-scale systems with teams of over 50 developers, precise commit hygiene is mandatory. When histories degrade into an unintelligible mess of vague messages, tracking the lifetime of issues becomes impossible.
By writing commits that neatly partition changes into logical sections with contextual details, project owners can streamline debugging, reduce coordination confusion, and simplify onboarding across an entire company.
Commit Message Style Guidelines
To maximize their usefulness, I structure commit messages following consistent style rules:
1. One-Line Summary
Start with a single line briefly summarizing the primary change, written as an imperative statement:
Refactor file upload to use streaming rather than buffering entire payload
Guidelines for effective one-line summaries:
- Concisely explain what the change is
- Start with active verb in imperative mood (Add, Remove, Refactor, etc)
- Less than 50 characters, ideally less than 70
- Use sentence case capitalization
2. Blank Line After Summary
Insert a blank line after the one-line summary before any other content. Visually separating the summary from details with a blank line makes skimming commit messages easy.
3. Paragraph Describing Changes
Use a paragraph after the typed summary to explain why changes were made and how the end result differs:
Refactor file upload to use streaming
The old implementation loaded entire payloads into server memory before writing to disk. For large uploads, this caused out of memory errors.
This commit refactors logic to stream uploads directly to disk instead, preventing server crashes with large files.
In the paragraph, aim to communicates important contextual details like:
- Background motivating the changes
- High-level technical details
- Benefits over old implementation
- Links to related issues/tickets
- Other helpful clarifying details
4. Lists of Issues Fixed and Breaking Changes
Call out specific issues addressed in the changes and any breaking changes with bulleted lists:
Upgrade Django to 4.1
Take advantage of new Django 4 features and fixes.
Issues addressed:
- #431: SQL injection vulnerability in ArticleViewSet
- #512: Switch to native tzinfo implementation
Breaking changes:
- Removed Python 3.9 support due to Django 4.1 incompatibilities
Being explicit about exactly which issues were targeted and what existing functionality no longer works due to the changes improves traceability and reduces unexpected surprises.
5. Body Wrap at 72 Characters
I wrap all commit message body content at 72 characters, which ensures logs render cleanly in terminals without overly long lines:
Refactor package manager to lazily resolve dependencies
The old dependency resolver would eagerly resolve all dependencies
on application startup, resulting in slow launch times for apps
with many transitive dependencies.
This commit refactors the algorithm to lazily resolve dependencies
only when required for a particular flow. This optimization
reduces average launch times by 350+ ms in testing.
72 character line length strikes the right balance between density of information and readability.
Comparing Styles: Conventional Commits vs. Angular
The two most common structured commit message styles I‘ve used professionally are Conventional Commits and Angular Commit Message Conventions.
Both formats share identical goals – creating cleanly partitioned commit logs that simplify release management, debugging, and collaboration. They primarily differ around mandating commit types and allowing scope descriptors.
Angular Commit Conventions
Angular‘s commit convention recommends the following format comprised of specific typed sections:
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
Common types are:
- feat: New feature
- fix: Bug fix
- docs: Docs update
- style: Formatting/missing semi colons
- refactor: Code change that neither fixes a bug nor adds a feature
- perf: Performance improvement
- test: Adding missing tests
- chore: Maintenance
Scope denotes what part of the system is affected. Subject contains succinct description of change using imperative, present tense.
A commit message following the Angular style:
feat(file-upload): add resumable upload capability
Large file uploads would frequently fail due to network issues. This change adds the ability to pause and resume uploads to improve reliability.
Fixes #832
Conventional Commits
The Conventional Commits specification is less prescriptive around section headers:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
It defines a mandatory type section with a constrained list of allowed values indicating commit intention:
- fix: Bug fixes
- feat: New feature
- build: Changes to build process
- ci: Continuous integration and deployment
- docs: Documentation
- style: Code style changes
- refactor: Code improvements
- perf: Performance enhancements
- test: Tests additions/improvements
For example:
fix: correct minor typo in API documentation
There was a small spelling mistake in the gift card API docs.
This patch fixes the typo.
fixes #456
While flexible, Conventional Commits trade-off requiring an indicative type prefix for every change.
Based on years applying both formats on production systems, I‘ve found Angular‘s model generally leads to marginally more readable histories by permitting types to be omitted when self-evident. However, Conventional Commit‘s mandatory indicative typing helps automate changelogs generation.
Advanced Git History Management Techniques
Beyond writing clear commit messages for individual changes, Git pros also incorporate tools to curate history. Common techniques include:
Squashing – Condensing many granular commits on a single feature branch into one logical commit.
Cherry-picking – Selectively pulling single commits from one branch into another feature branch.
Rebasing – Updating feature branches with latest upstream changes by re-writing commit history.
Fixup commits – Amending previous commits instead of adding new ones.
Revert commits – Explicitly undoing previous commits.
For example:
fixup! refactor authentication logic
This amends the previous commit to refactor
authentication with two missing dependency updates
revert: add elastic search indexes
This reverts a previous commit that introduced
buggy search functionality by accident.
I heavily rely on advanced commands like rebase, squash, fixup, and revert while developing locally to curate an expressive project history before pushing branches to remote repositories for collaboration.
While communicating context via messages stays important with server-side history rewrites, client-side protocols like Git allow engineers incredible freedom edit histories to tell clean and coherent stories.
Incorporating Linting and Validation
Given the ubiquity Git, a thriving ecosystem of tooling has emerged to help enforce commit conventions through validation and linting. My teams actively use commitlint, commitizen, and husky in our CI pipelines.
commitlint checks that commit messages match a specified pattern defined through formal configuration. Teams define validation rules requiring certain commit types or prefixes.
Helper tools like commitizen simplify authoring valid commits through an interactive CLI asking validated questions to generate properly formatted messages.
Pre-commit hooks implemented with husky automatically run linters at commit-time preventing low-quality commit text or those failing defined patterns from entering history.
Relying on automation allows us to shift cognitive load away from manually maintaining high commit hygiene standards to focusing deeper on domain problems.
Cultivating Commit Discipline on Teams
My role as a senior full-stack developer involves code reviewing new team member‘s pull requests and mentoring around commit best practices.
Common recurring issues I‘ve observed undermining commit quality:
1. Vague commit summaries
Summaries should concisely explain primary change, not just reference attached issue ticket:
// Vague
Fixed login bug
// Better
Ensure login redirects to dashboard on success
2. Mixing multiple logical changes into single commits
Each commit should encompass related set of changes representing single purpose:
// Overly complex single commit
Adds password validation rules; Fixes login routing issues; Updates docs
// Split correctly
Add password validation for strength
Fix login routing for IE11
Update docs for new password rules
3. Using end-of-sentence periods in summaries
Summaries written in active imperative mood should omit end punctuation:
// Don‘t use end period
Fix broken link checker.
// Correct
Fix broken link checker
4. Present instead of past tense
Summaries describe commit changes themselves, thus stay in present tense:
// Not past tense for commit summary
Fixed artifact downloader bugs
Introduced CI optimizations
// Correct tense
Fix artifact downloader bugs
Introduce CI optimizations
Guide developers struggling with quality commits towards resources like this guide. For team repositories, provide custom commit template snippets prompting helpful headers and sections. Though establishing sound commit habits requires continued reinforcement, benefits towards long-term maintainability are invaluable.
References and Further Reading
For readers interested more deeply applying professional source control techniques, I highly recommend:
- Pro Git Book – Commit Guidelines Section
- Conventional Commits Specification
- Chris Beams – How to Write a Git Commit Message
- Udacity Git Commit Message Style Guide
- 5 Useful Tips for a Better Commit Message
With some concerted focus establishing sound habits early on, teams can reap substantial dividends through evergreen commit histories that elegantly capture the full lifetime of a project. Let me know if you have any other favorite tips for encouraging better commit quality!


