How to Write Effective Git Commit Messages
A practical English guide to writing clear, professional Git commit messages — conventions, examples, and common mistakes to avoid.
Git commit messages are one of the most-written but least-thought-about forms of technical English. A well-written commit message tells the story of your codebase. A poorly written one — “fix stuff”, “WIP”, “asdfgh” — creates a history that is useless to your team and your future self.
This guide covers the conventions, vocabulary, and specific language choices that make commit messages genuinely useful.
The Conventional Commit Format
The most widely adopted standard is Conventional Commits, which uses a structured prefix:
<type>(<scope>): <short description>
[optional body]
[optional footer]
Example:
feat(auth): add JWT refresh token endpoint
Implements a /auth/refresh endpoint that accepts a valid refresh token
and returns a new access token. Refresh tokens expire after 7 days.
Closes #142
Commit Types and When to Use Them
| Type | Use when | Example |
|---|---|---|
feat | Adding a new feature | feat(search): add full-text search to products |
fix | Fixing a bug | fix(api): handle null response from payment gateway |
docs | Documentation only | docs(readme): update local setup instructions |
style | Formatting, no logic change | style(button): apply consistent border-radius |
refactor | Code restructure, no feature or fix | refactor(user): extract validation into separate module |
test | Adding or updating tests | test(cart): add edge case for empty cart checkout |
chore | Maintenance tasks | chore(deps): upgrade eslint to v9 |
perf | Performance improvement | perf(query): add index to reduce user lookup latency |
ci | CI/CD configuration | ci(github): add step to run integration tests |
revert | Reverting a previous commit | revert: feat(auth): add JWT refresh token endpoint |
Writing the Subject Line
The subject line (the first line) is the most important part. Rules:
- Use the imperative mood: “Add feature” not “Added feature” or “Adding feature”
- Keep it under 72 characters
- Don’t end with a period
- Capitalise the first word after the type prefix
Imperative mood examples:
| Wrong | Right |
|---|---|
| ”Fixed the login bug" | "fix(auth): resolve login failure on empty password" |
| "I added tests" | "test(user): add unit tests for email validation" |
| "Changes to config" | "chore(config): move timeout settings to env variables" |
| "Updated README" | "docs(readme): add Docker setup instructions” |
The imperative mood feels like completing the sentence: “This commit will… [add a refresh token endpoint]”.
Writing the Body
The body is optional but valuable for non-obvious changes. Use it to explain:
- Why the change was made (not just what changed — that’s visible in the diff)
- The problem that existed before
- Trade-offs or decisions made
Good body examples:
“Previously, all database connections were created per-request. This caused connection pool exhaustion under high load. This commit implements connection pooling via pg-pool, capping connections at 20 per instance.”
“The previous implementation used a synchronous file read, which blocked the event loop during large uploads. Switching to the async stream API resolves the performance issue reported in #234.”
What not to put in the body:
- Repetition of what the diff already shows
- Vague statements like “made some improvements”
- Personal notes (“not sure if this is the right approach”)
Referencing Issues and Pull Requests
Most teams use keywords that automatically close issues when the commit is merged:
Closes #142— closes the issue
Fixes #89— closes the issue (also implies it was a bug)
Refs #201— references without closing
See also #98— additional context
Common Commit Message Mistakes
| Mistake | Why it’s a problem | Fix |
|---|---|---|
| ”fix” | Tells you nothing | ”fix(api): return 404 when user not found" |
| "WIP” | Not a complete thought | Squash WIP commits before merging |
| ”fixed a bug” | Past tense, vague | ”fix(checkout): prevent duplicate order submission" |
| "Updating stuff” | Completely meaningless | ”chore(deps): upgrade React to 18.3” |
| Very long subject | Hard to read in git log | Keep under 72 characters |
Useful Verbs for Commit Messages
Technical commit writing uses a specific set of verbs consistently:
add, remove, update, fix, refactor, extract, move, rename, replace, implement, introduce, deprecate, revert, disable, enable, configure, document, optimise
A clear commit history is not cosmetic. It is what allows your team to git blame, git bisect, and understand the evolution of the codebase years later. Write messages for the engineer who will read them at 11pm during an incident — that engineer might be you.