Git Commit Message Best Practices
Do you ever find yourself at a loss when writing commit messages? Especially when you’re not working on any major features! But you still want to keep track of your commit history! That way, you can see everything at a glance. It’s not just for your own reference—it’s even more useful for the team, serving as a detailed guide just like a manual! If this sounds like what you’re looking for, keep reading.
Writing commit messages is not just about recording changes. In real engineering workflows, commit history is treated as a communication layer between developers over time.
A well-structured commit history helps with:
- Debugging production issues faster
- Making code reviews more efficient
- Automating changelog generation
- Long-term maintainability
- Clear team collaboration
1. Standard Commit Format (Conventional Commits)
Most modern engineering teams follow the Conventional Commits specification:
feat(auth): add Google OAuth login support
Integrated Google OAuth 2.0 using passport-google-oauth20. Users can now sign in with their Google account on the login page.
2. Commit Types — What Each One Means
3. The Golden Rules
Write in imperative mood — like a command. Think of it this way: your commit message completes the sentence "If applied, this commit will..."
# ✅ Good
feat(cart): add quantity selector to product card
fix(api): handle null response from payment gateway
refactor(utils): extract date formatting into helper
# ❌ Bad
added quantity selector
fixed a bug
some refactoring4. Scope — Be Specific
The scope (the part in parentheses) tells your team where the change happened. Keep scopes consistent across your team.
feat(auth): ... # authentication system
fix(dashboard): ... # dashboard page
chore(deps): ... # dependencies
style(button): ... # Button component
test(api/user): ... # user API testsKeep scopes consistent across your team. If your repo uses auth, don't mix in authentication.
5. Writing a Good Body
For non-trivial changes, add a body after a blank line. Explain why, not what — the diff already shows what changed.
refactor(store): replace Redux with Zustand for cart state
# No behavior changes — all cart operations remain identical.
6. Referencing Issues and PRs
Most teams link commits to tickets automatically.
fix(checkout): prevent double submission on slow networksGitHub and GitLab will auto-close the linked issue when this commit lands on the main branch.
7. Real Workflow Example
Work incrementally and commit as you go. Before merging, clean up if needed with rebase.
# Start your branch
git checkout -b feat/user-avatar-upload
# Work incrementally, commit as you go
git commit -m "feat(avatar): add file input to profile settings page"
git commit -m "feat(avatar): upload image to S3 on form submit"
git commit -m "feat(avatar): display uploaded avatar in nav and profile"
git commit -m "test(avatar): add unit tests for upload handler"
git commit -m "fix(avatar): handle unsupported file type with error message"
# Before merging, clean up if needed
git rebase -i origin/mainAfter merging, your git log tells a clear story — no detective work needed.
8. Bad Commits to Avoid
Vague messages like "fix", "wip", "changes", "update stuff", or "FINAL FINAL" are useless to anyone — including future you at 2am debugging a production incident.
# ❌ These destroy your history
git commit -m "fix"
git commit -m "wip"
git commit -m "asdfgh"
git commit -m "changes"
git commit -m "update stuff"
git commit -m "final"
git commit -m "FINAL FINAL"
These are useless to anyone — including future you at 2am debugging a production incident.
9. Enforce It with Tooling
Don't rely on discipline alone. Automate the standard:
# Install commitlint + husky
npm install --save-dev @commitlint/cli @commitlint/config-conventional husky// commitlint.config.js
export default {
extends: ["@commitlint/config-conventional"],
};# Set up the commit-msg hook
npx husky init
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msgNow if anyone on the team pushes a bad commit message, the hook rejects it before it goes anywhere.
Summary
Clean commit history is a sign of a professional engineering team. It costs 30 extra seconds per commit and saves hours during every incident, review, and onboarding.