The Art of Writing Meaningful Git Commit Messages

Author

theurikarue

Published

May 6, 2026

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 form

  • Maintenance: It is much easier to undo certain modifications. > git log --oneline shows fix: Add input validation for email field → you know exactly which commit to revert. > A vague fixed bug leaves 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 Redis tells a new teammate why caching exists. > refactor: Extract payment processing into service class explains 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 commit

This 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 --amend

Note: 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

  1. 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 are

  2. Capitalize the subject line.

    fix: Resolve memory leak in websocket handler

    fix: resolve memory leak in websocket handler

  3. Do not end the subject line with a period.

    docs: Add contributing guide

    docs: Add contributing guide.

  4. 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.
  5. 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.

  6. 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 LoginController

  7. Use 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.