Git Commit Messages: Best Practices and English Tips
How to write clear, consistent, professional git commit messages in English — with the Conventional Commits format, real examples, and the most common mistakes to avoid.
Your commit history is a log of decisions written in English. Every developer on your team — now and in the future — will read it. A clear commit history is documentation. A messy one is noise.
Writing good commit messages is a writing skill, not just a Git skill. Here is everything you need to do it well.
Why Commit Messages Matter
A bad commit history looks like this:
fix
wip
asdf
changes
fixed the thing
updated
more work on login
final fix (for real this time)
A good commit history tells a story:
feat(auth): add JWT refresh token rotation
fix(api): return 422 instead of 500 on invalid email format
docs(readme): document environment variable setup for local dev
refactor(cart): extract price calculation into a pure function
chore(deps): upgrade React from 18.2 to 18.3
The difference is not skill level — it is habit and practice.
The Conventional Commits Format
Conventional Commits is the most widely used standard for commit message formatting in professional teams. The pattern is:
<type>(<scope>): <short description>
[optional body]
[optional footer: closes #123, breaking change note, etc.]
The most common types
| Type | When to use it |
|---|---|
feat | A new feature |
fix | A bug fix |
docs | Documentation changes only |
style | Formatting, whitespace — not CSS styling |
refactor | Code restructured without changing behaviour |
test | Adding or fixing tests |
chore | Maintenance: dependency updates, CI config, build scripts |
perf | Performance improvement |
revert | Reverting a previous commit |
The scope (optional but useful)
The scope is the part of the codebase affected: auth, api, ui, db, cart, payments, ci, readme…
fix(auth): handle expired tokens gracefully
refactor(db): replace raw queries with ORM methods
Writing the Subject Line
The subject line is the single most important part of the commit message. It appears in git log, pull request lists, and code review tools.
Rules for the subject line
-
Use the imperative mood — write as if giving a command
✅add user authentication
❌added user authentication
❌adding user authentication -
Keep it under 72 characters — longer titles get truncated in many tools
-
Start with the type, then a colon, then the description
feat(payments): add Stripe webhook handler -
Describe what the commit does, not how
✅fix(api): prevent duplicate email registration
❌fix(api): added if-statement check for existing email -
Don’t end with a period
❌fix: correct typo in error message.
✅fix: correct typo in error message
The Imperative Mood: Why It Matters
The imperative mood means writing a verb as if giving an instruction. Think of it as: “If applied, this commit will ___.”
- ✅ “If applied, this commit will add JWT authentication”
- ✅ “If applied, this commit will fix the null pointer on login”
- ✅ “If applied, this commit will remove deprecated API endpoints”
Imperative vs. other verb forms
| ❌ Wrong | ✅ Correct (imperative) |
|---|---|
| added password validation | add password validation |
| fixing memory leak in cache | fix memory leak in cache |
| updated dependencies | update dependencies |
| new feature: dark mode | feat(ui): add dark mode toggle |
| changes to improve performance | perf(db): add index on user email column |
Common English Verbs for Commit Messages
You do not need a large vocabulary. These verbs cover most situations:
| Verb | Use for |
|---|---|
| add | New feature, file, endpoint, test, dependency |
| fix | Bug fix |
| update | Modifying something that already exists |
| remove / delete | Removing code, files, endpoints |
| refactor | Restructuring code without changing behavior |
| rename | Renaming files, functions, variables |
| improve | Making something better (vague — try to be more specific) |
| extract | Moving code into a separate file or function |
| merge | Merging branches (usually auto-generated) |
| revert | Undoing a previous change |
| bump | Version number increase (common for chore/deps commits) |
| migrate | Moving data or code to a new format/location |
Writing the Commit Body (For Complex Changes)
For simple changes, the subject line is enough. For complex refactors, architectural changes, or non-obvious fixes, add a body.
The body answers: Why was this change made? Not what — the diff shows what. The body explains the reasoning.
fix(auth): prevent session fixation on login
Previously, the session ID was not regenerated after successful
authentication, making the app vulnerable to session fixation attacks.
This change calls `req.session.regenerate()` immediately after
the user credentials are validated, before writing any data to the
session.
Closes #487
Related to #423 (auth hardening sprint)
Useful phrases for commit bodies
“Previously, this code…” — explains what was happening before
”This change…” — explains what you changed and why
”This fixes…” / “This resolves…” — links to the problem
”Note: this is a breaking change — callers must update…” — flags impact
”Closes #123” / “Fixes #456” — links to a GitHub issue (auto-closes it on merge)
Rewriting Bad Commit Messages: Practice
Try to improve these before reading the corrections:
Bad: fix stuff in auth
Better: fix(auth): redirect to /login after session timeout
Bad: wip
Better: feat(search): implement fuzzy search using fuse.js (in progress) — or, even better, use a branch and squash before merging.
Bad: updated the thing
Better: chore(ci): update Node.js version to 22 in GitHub Actions workflow
Bad: changes for sarah
Better: refactor(reports): split CSV export into a background job per Sarah's review
When the “Right” Format Doesn’t Matter
Not every commit needs Conventional Commits format. On a solo project, a small team with a different convention, or a prototype, strict formatting may be overkill. What always matters:
- The subject line tells you what happened without reading the diff
- You were not lying — the message describes what the commit actually does
The first commit — the one that starts a new project — is traditionally just:
initial commit
No type, no scope. Everyone understands it.
Good commit messages are a professional signal. When a senior developer reads your git history and sees clear, consistent, informative messages, they trust your work before they read a single line of code.