The Art of Writing Meaningful Git Commit Messages
As you already know, I spent the last three months of my life in the JKUAT internal attachment program, and the articles I’ve written so far have shared the experiences I’ve had and the lessons I’ve learnt while there.
Today, I would like to talk about writing meaningful git commit messages. Don’t worry, it’ll not be a long article. I tend to think there’s nothing as hard as coming up with names for variables when it comes to programming and I believe the things that comes second is coming up with meaningful git commit messages.
On a personal note looking back at my old commits, I found a message from May 21st, 2024: > “Tweaked a few things” — not exactly helpful!
Why Should You Write Better Commit Messages?
Git enters a whole other realm the moment you start working in teams. I challenge you to open up a personal project, or any repository for that matter, and run git log to view a list of old commit messages. You’ll get to realize that six months later, you definitely cannot remember what the few things you tweaked were.
Now imagine if you were working in a team; do you think they would know what you meant by ‘Tweaked a few things’? Probably not.
Key Benefits:
Clarity: Future readers will not have trouble understanding what changed and why. > Instead of:
fix stuff> Write:fix: Prevent crash when user submits empty formMaintenance: It is much easier to undo certain modifications. >
git log --onelineshowsfix: Add input validation for email field→ you know exactly which commit to revert. > A vaguefixed bugleaves you guessing what got changed.Documentation: Simplifies making changes to release notes. >
feat: Add OAuth2 login+fix: Resolve token refresh race condition→ paste directly into changelog.Roadmapping: Clear messages serve as a roadmap for future developers. >
perf: Cache database queries in Redistells a new teammate why caching exists. >refactor: Extract payment processing into service classexplains the architectural intent.
The Anatomy of A Good Commit Message
Basic
git commit -m "feat: Add user registration endpoint"Detailed
git commit -m "feat: Add user registration endpoint" -m "
- Implement POST /api/register with email + password
- Validate input and hash passwords with bcrypt
- Return JWT token on successful registration
"You can also use a text editor for multi-line messages:
git commitThis opens your default editor (vim, nano, etc.) where you can write:
feat: Add user registration endpoint
Implement the registration flow including:
- POST /api/register endpoint with email + password validation
- Password hashing using bcrypt before storing to database
- JWT token generation returned on successful registration
- Duplicate email detection returning 409 Conflict
Closes #42
Amending Mistakes
In case you make an error in a recent commit, you can run:
git commit --amendNote: It’s great to use amend to clean up local commits. However, it’s best to refrain from changing commits that have already been made public.
Conventional Commits
Conventional commits provide a lightweight convention on top of commit messages. Here’s a great template:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Commit Types
| Type | Description | Example |
|---|---|---|
| feat | Adds a new feature | feat: Add dark mode toggle to settings |
| fix | Fixes a bug | fix: Handle null pointer in user profile fetch |
| refactor | Rewrites/restructures code without fixing bugs or adding features | refactor: Extract authentication logic into middleware |
| chore | Miscellaneous (updates dependencies, .gitignore, etc.) |
chore: Bump axios from 1.6.0 to 1.7.2 |
| perf | Special refactor aimed at improving performance | perf: Lazy-load images below the fold |
| docs | Changes to documentation (e.g., README) | docs: Update API usage examples in README |
| style | Formatting changes (whitespace, semi-colons) | style: Format code with Prettier |
| test | Adding or correcting tests | test: Add unit tests for payment validator |
| ci/ops | CI/CD and infrastructure components | ci: Add GitHub Actions workflow for automated testing |
Scope (Optional)
A scope annotates which part of the codebase the commit touches:
| Example | Meaning |
|---|---|
feat(auth): Add password reset flow |
Changes in the auth module |
fix(cart): Correct tax calculation |
Bug fix in the shopping cart |
feat(api): Add rate limiting |
API-layer change |
refactor(db): Migrate from MySQL to PostgreSQL |
Database-layer refactor |
Breaking Changes
Use ! or BREAKING CHANGE footer to signal breaking changes:
feat(api)!: Change authentication endpoint from /v1/auth to /v2/auth
feat: Switch from JWT to session-based auth
BREAKING CHANGE: All existing tokens are invalidated. Clients must re-authenticate.
Rules for Creating a Great Commit Message
Limit the subject line to 50 characters.
✅
feat: Add password strength indicator❌
feat: Add a new password strength indicator to the registration form so users can see how strong their passwords areCapitalize the subject line.
✅
fix: Resolve memory leak in websocket handler❌
fix: resolve memory leak in websocket handlerDo not end the subject line with a period.
✅
docs: Add contributing guide❌
docs: Add contributing guide.Separate the subject from the body with a blank line.
✅ feat: Add login rate limiting ← blank line Implement rate limiting on the login endpoint to prevent brute-force attacks. Limits to 5 attempts per minute per IP.❌ feat: Add login rate limiting Implement rate limiting on the login endpoint to prevent brute-force attacks. Limits to 5 attempts per minute per IP.Wrap the body at 72 characters.
✅ Lines are kept short and readable in
git log.❌ Long lines get wrapped awkwardly by the terminal and are hard to read in
git log.Use the body to explain what and why (not how).
✅
Why: Rate limiting prevents brute-force attacks on user accounts❌
How: Created RateLimiter class, called checkLimit() in LoginControllerUse the imperative mood in the subject line.
✅
feat: Add unit tests❌
feat: Added unit tests(past tense)❌
feat: Adding unit tests(gerund)❌
feat: Adds unit tests(third person)Imagine completing the sentence: “If applied, this commit will _____”
- ✅ “…add unit tests”
- ❌ “…added unit tests”
- ❌ “…adding unit tests”
Putting It All Together
Here’s a real-world example combining all the rules:
feat(auth): Add password reset flow
Allow users to reset their passwords via email verification.
This reduces support tickets for forgotten passwords.
Closes #87
- Subject:
feat(auth)(type + scope), “Add password reset flow” (imperative, capitalized, no period, ≤ 50 chars) - Body: explains what (password reset via email) and why (reduce support tickets), wrapped at 72 chars
- Footer:
Closes #87(links to the GitHub issue)
Writing good commit messages is an extremely beneficial skill to develop. Commits serve as an archive of changes—an ancient manuscript to help us decipher the past and make reasoned decisions in the future.