The Complete Guide to Writing Better Commit Messages

Published May 3, 2026 · BeLikeNative Team · 11 min read

Your commit history is a form of documentation. Six months from now, when someone runs git blame on a confusing line of code, the commit message is the first thing they will read. If that message says "fix stuff" or "WIP," they learn nothing. If it says "fix race condition in session refresh when token expires during request," they understand the intent immediately.

Good commit messages are not about being pedantic. They are about respecting your future self and your teammates' time. A well-written commit history turns git log into a readable changelog that explains not just what changed, but why.

Why Commit Messages Matter

There are three moments when commit messages become critical:

  1. Code review: Reviewers read the commit message before the diff. A good message frames the change and tells the reviewer what to look for.
  2. Debugging: git bisect is only useful when commit messages describe what each change does. If every message is "update," bisect finds the commit but tells you nothing about it.
  3. Release notes: If your project uses automated changelogs (and it should), commit messages become user-facing documentation. "feat: add dark mode toggle" generates a clear changelog entry. "changes" does not.

The Conventional Commits Format

Conventional Commits is a specification that adds structure to commit messages. It is used by Angular, Vue.js, Ionic, and thousands of other projects. The format is:

<type>(<scope>): <description>

[optional body]

[optional footer(s)]

Types:

Scope is optional and describes the area of the codebase: feat(auth): add OAuth2 login or fix(api): handle null response from payment provider.

The Imperative Mood

The single most important style rule for commit messages: use the imperative mood in the subject line. Write "Add feature" not "Added feature" or "Adds feature."

The imperative mood reads as a command or instruction. It matches what Git itself uses: when you merge, Git creates "Merge branch 'feature'," not "Merged branch." Your commits should follow the same pattern.

A useful trick: your commit message should complete the sentence "If applied, this commit will ___." "If applied, this commit will add OAuth2 login" works. "If applied, this commit will added OAuth2 login" does not.

Why Non-Native Speakers Struggle with Imperative Mood

The imperative mood is genuinely difficult for non-native English speakers because it looks identical to the base form of the verb. In many languages, the imperative has a distinct conjugation that marks it clearly. In English, "Add" (imperative) looks exactly like "Add" (infinitive) and "add" (present simple for I/you/we/they). This ambiguity means there is no visual signal that the imperative is being used.

Additionally, many languages default to the past tense for describing completed actions, which is what a commit represents. In German, you might naturally write "Funktion hinzugefuegt" (function added). The mental translation often keeps the past tense.

The fix is mechanical: before committing, re-read your message and check if it starts with a bare verb. "Add," "Fix," "Remove," "Update," "Refactor," "Handle." If it starts with "Added," "Fixed," "Removing," or a noun, rewrite it.

10 Before-and-After Examples

Before
"fixed bug"
After
"fix(auth): prevent session expiry during active WebSocket connection"
Before
"update"
After
"refactor(db): replace raw SQL queries with parameterized statements"
Before
"WIP"
After
"feat(cart): add quantity selector to product detail page"
Before
"changes to the login page"
After
"fix(ui): correct tab order on login form for keyboard navigation"
Before
"Added tests"
After
"test(api): add integration tests for rate limiting middleware"
Before
"misc fixes"
After
"fix(validation): reject negative quantities in order form"
Before
"Refactoring done"
After
"refactor(notifications): extract email template rendering into separate service"
Before
"more work on feature"
After
"feat(search): implement fuzzy matching for product name search"
Before
"oops"
After
"fix(build): add missing dependency for PDF generation module"
Before
"final version"
After
"docs(api): add request/response examples to authentication endpoints"

Commit Message Rules to Adopt

  1. Subject line under 72 characters. This is a hard limit for many Git tools. git log --oneline truncates at 72.
  2. Capitalize the first word. "Fix bug" not "fix bug" (some teams prefer lowercase with Conventional Commits; pick one and be consistent).
  3. No period at the end of the subject. The subject line is a title, not a sentence.
  4. Blank line between subject and body. Git uses this to distinguish the summary from the description.
  5. Body explains "why," not "what." The diff shows what changed. The body should explain the reasoning, the trade-offs, and the alternatives considered.
  6. Reference issues. Add "Closes #123" or "Refs #456" in the footer to link commits to issue trackers.

Tools for Enforcing Commit Standards

Humans are inconsistent. The way to get consistent commit messages is to enforce them with tooling.

For teams where non-native English speakers frequently contribute, the bln-commit-lint GitHub Action adds language-aware checking on top of format validation. It catches common L1 transfer errors in commit messages (like past tense instead of imperative, or missing articles) and suggests corrections inline in the PR.

Enforce Commit Standards Automatically

Catch format violations and language issues in commit messages on every PR.

Commit Lint Action