Conventional Commits: The Complete Guide with Cheatsheet
I’ve read the full article. Here’s a complete rewrite with the cheatsheet table, restructured H2s, updated meta, and internal links woven in naturally.
Are your commit messages still "fix stuff", "update", or "final FINAL v3"? You’re losing something real — readable history, automated changelogs, and a team that can debug without you.

Conventional Commits is a one-page specification that fixes all of this. This guide covers everything: the format, every type, a ready-to-paste cheatsheet, and how to enforce it automatically in your repo.
Part of the Git series on this blog.
If you’re new to Git, start with Git Explained Simply before continuing. Already comfortable? The companion post Committing with Intention digs into the art of a good commit — pair it with this spec guide.
What Are Conventional Commits?
Conventional Commits is a lightweight convention on top of commit messages. It gives every commit a machine-readable type, an optional scope, and a human-readable description — all in one line.
Basic structure:
<type>[optional scope]: <description>
[Optional body]
[optional footer(s)]Example:
feat(auth): add OAuth2 login via Google
Replaces the legacy username/password-only flow.
Closes #88That single line tells your CI pipeline, your changelog generator, and your future self exactly what happened and why.
Conventional Commits Cheatsheet (All Types at a Glance)
Copy this into your team wiki or pin it to your Notion.
| Type | When to Use | Version Bump | Example |
|---|---|---|---|
feat | A new feature for the user | minor | feat(cart): add coupon code support |
fix | A bug fix for the user | patch | fix(api): handle null response from payment |
docs | Documentation changes only | none | docs: update README with setup steps |
style | Formatting, whitespace, semicolons (no logic) | none | style: reformat auth module |
refactor | Code restructure — no new feature, no bug fix | none | refactor(db): extract query builder helper |
perf | Performance improvement | patch | perf(img): switch to WebP for thumbnails |
test | Adding or fixing tests | none | test(auth): add edge cases for token expiry |
build | Build system or external dependency changes | none | build: upgrade webpack to v5 |
ci | CI/CD config changes | none | ci: add lint step to GitHub Actions |
chore | Maintenance that doesn’t fit anywhere else | none | chore(deps): update Node.js to v22 |
revert | Reverts a previous commit | patch | revert: feat(auth): add OAuth2 login |
Breaking change? Add
!after the type orBREAKING CHANGE:in the footer.feat(api)!: remove deprecated v1 endpoints→ triggers a major version bump.
The Three Parts of a Conventional Commit
1. The Header (Required)
The header is the only required line. It follows:
<type>[(scope)]: <description>- type — from the cheatsheet above
- scope — optional, in parentheses, names the module/area (
auth,ui,api) - description — present-tense, lowercase, no period at the end
# Good
feat(checkout): add Apple Pay support
# Bad — past tense, vague, no type
Added some payment stuff2. The Body (Optional, Recommended for Non-Trivial Changes)
Use the body to explain why, not what — the diff already shows what changed.
fix(token): use UTC for all expiry comparisons
Token validation was failing for users in UTC+5:30 because the
server was comparing local time. Standardising on UTC fixes this.Leave a blank line between the header and body.
3. The Footer (Optional)
Use footers for two things: referencing issues and declaring breaking changes.
feat(api)!: replace REST endpoints with GraphQL
BREAKING CHANGE: All /v1/ REST routes have been removed.
Migrate to the GraphQL endpoint at /graphql.
Refs: #201
Closes #198Fixes #N and Closes #N will automatically close the GitHub issue when the PR is merged.
For even richer commit metadata, see Git Trailers: The Commit Metadata Most Developers Skip — a natural extension of conventional footers.
How Conventional Commits Power Automation
This is where the format earns its keep. Each type maps to a semantic versioning action:
feat → minor bump (1.2.0 → 1.3.0)
fix/perf → patch bump (1.2.0 → 1.2.1)
BREAKING → major bump (1.2.0 → 2.0.0)
docs/chore/ci/style → no bumpTools that plug into this automatically:
- semantic-release — reads your commits, bumps the version, generates a
CHANGELOG.md, and publishes — zero manual input - conventional-changelog — generates changelogs from your history
- release-please (by Google) — creates release PRs automatically on GitHub
How to Enforce Conventional Commits in Your Repo
You don’t want this to be voluntary. Automate the gate.
commitlint + Husky (Recommended)
npm install --save-dev @commitlint/cli @commitlint/config-conventional husky
commitlint.config.js:
module.exports = { extends: ['@commitlint/config-conventional'] };Set up the commit-msg hook:
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'Now every commit message is validated before it lands. Bad format = blocked commit.
Commitizen (Interactive CLI)
If your team finds the format hard to remember, Commitizen gives them a guided prompt:
npm install -g commitizen
npx commitizen init cz-conventional-changelog --save-devThen run git cz instead of git commit.
VS Code Extension
Conventional Commits adds a guided commit UI directly in the editor sidebar. Good for onboarding juniors.
Conventional Commits vs Semantic Versioning — What’s the Difference?
They’re not the same thing, but they’re designed to work together.
| Conventional Commits | Semantic Versioning (SemVer) | |
|---|---|---|
| What it is | A format for commit messages | A format for version numbers |
| Scope | Your git history | Your package/release tags |
| Who reads it | Humans + tooling | Humans + dependency managers |
| Example | feat: add search | 1.4.0 |
Conventional Commits is the input. SemVer is the output. Tools like semantic-release do the translation automatically.
Linking Commits to Issues and Jira Tickets
Conventional Commits plays nicely with project tracking:
GitHub Issues:
fix(login): handle expired session redirect
Fixes #312Jira:
feat(dashboard): add export to CSV
Refs: PROJ-456The Fixes keyword closes the issue automatically on merge. Refs just links without closing.
Practical Examples (Copy & Paste)
# New feature with scope
feat(payments): integrate Razorpay checkout
# Bug fix
fix(auth): prevent redirect loop on token expiry
# Breaking change
feat(api)!: remove XML response format — JSON only
# Dependency update
chore(deps): bump lodash from 4.17.20 to 4.17.21
# Docs update
docs(contributing): add commit message guidelines
# Test coverage
test(cart): add edge cases for zero-quantity items
# CI pipeline change
ci: cache node_modules in GitHub Actions workflow
# Performance
perf(images): lazy-load hero images below the foldYour Next Steps
- Bookmark the cheatsheet table above
- Add
commitlint + huskyto your next project - Try
git czfor one week — it builds the habit fast - Once your history is clean, wire up
semantic-releaseand let your CI handle versioning
Continue Learning — The Git Series
This post covers the commit format. For the full picture on writing commits that are genuinely useful to read later, go here:
- Committing with Intention — The art of a good commit (Part 2 of the Git Mastery Series)
- How Git Actually Thinks — The mental model most developers are missing (Part 1)
- Branching Without Fear — Where conventional commit types become even more powerful (Part 3)
- Git Debugging Like a Pro — How a clean commit history makes
git bisecttrivial - Git Mastery: The Complete Series — Full index






3 Comments