How to use this generator
Choose a type from the dropdown — the hint line underneath tells
you exactly when each one belongs, so refactor and
chore stop being a coin flip. Add a scope if your
project uses them, write the subject in imperative mood, and watch
the counter: green under 50 characters, amber past it, red where
git's UI will start truncating you.
Flip the breaking-change toggle and the tool adds the
! and pre-fills a BREAKING CHANGE: footer
for you to finish. The preview on the right assembles everything to
spec — blank lines included — and Ctrl/⌘+Enter copies it anywhere
in the form. Paste into git commit, or pipe straight
into git commit -m "$(pbpaste)" if you're feeling
fancy.
What Conventional Commits actually are
Conventional Commits is a lightweight convention for writing git
commit messages so that both humans and machines can read
them. The shape is tiny —
type(scope)!: description, an optional body, an
optional footer — but that small discipline unlocks a lot.
It grew out of the AngularJS commit guidelines of the mid-2010s,
was formalized at conventionalcommits.org, and
reached version 1.0.0 in 2019. The core promise: your commit
history becomes structured data. Tools like
semantic-release and
standard-version read the types and derive
everything else — feat commits bump the
minor version, fix commits bump the
patch, and anything flagged
BREAKING CHANGE bumps the major. Version
numbers and changelogs stop being a Friday afternoon ritual and
start being a side effect of commits you were writing anyway.
Even without automation, the payoff is a
git log --oneline that reads like a table of contents:
fix(api): handle empty gateway responses tells a
reviewer what changed, where, and why it matters — before a single
diff line loads. Six months later, when someone asks "when did the
auth flow change?", the answer is one grep away instead of an
archaeology project.
Anatomy of a Conventional Commit
A fully-loaded commit, every part labeled:
# ┌ type ┌ scope ┌ breaking flag # │ │ │ ┌ description (≤50 chars, imperative) feat(auth)!: rotate refresh tokens on every use # body — the WHY, wrapped at ~72 chars, separated by a blank line Reusing refresh tokens let a leaked token grant access indefinitely. Tokens are now single-use; the old one is invalidated the moment a new one is issued. # footer — breaking details + issue references BREAKING CHANGE: /auth/refresh no longer returns the same token Closes #234
Three structural rules do most of the work: the description is
always on the first line, the body is always separated by a blank
line, and footers use the Token: value or
Token #value shape. Get those right and every tool in
the ecosystem — commitlint, changelog generators, release bots —
understands you without configuration.
The eleven types, decoded
feat— a new user-facing feature. Bumps the minor version under semantic-release.fix— a bug fix. Bumps the patch version.docs— documentation only: READMEs, guides, code comments. No code changed.style— formatting: whitespace, semicolons, lint fixes. Nothing that changes logic — and despite the name, CSS work is usually afeatorfix.refactor— production code restructured without changing behavior: renaming, extracting, simplifying.perf— a change whose whole point is performance.test— adding missing tests or correcting existing ones. No production code.build— the build system and dependencies: bundler config, package upgrades, compile scripts.ci— CI configuration: GitHub Actions, GitLab pipelines, Jenkinsfiles.chore— housekeeping that touches neither production code nor tests: tooling, editor config, tidying.revert— undoing a previous commit. Subject isrevert: <original subject>; the body carriesThis reverts commit <hash>.
Why teams actually adopt this
- Automatic changelogs — conventional-changelog walks the log and writes release notes grouped by type. No more "what even shipped this sprint?"
- Hands-off versioning — semantic-release derives SemVer from commit types and publishes without a human touching a version file.
- Enforcement at commit time — commitlint plus a husky pre-commit hook rejects non-conforming messages before they enter history, gently but firmly.
- Monorepo sanity — scopes tell tooling which packages a commit touched, so CI builds and tests only what changed.
- Faster reviews — reviewers know what kind of change to expect before opening the diff, and "why is this a refactor and not a feat?" becomes a one-line conversation.
Gotchas that trip up every team once
- Imperative mood, always. "add feature", never "added feature" or "adds feature". It matches git's own generated messages — "Merge branch", "Apply patch".
- 50 and 72 are real limits.
git log --onelinetruncates subjects around 50 characters; GitHub's UI wraps the body at 72. The counter above enforces the first one out of respect for your teammates' terminals. - No trailing period on the subject. It's a headline, not a sentence. Save the punctuation budget for the body.
styleis not about CSS. It means code formatting. This single confusion produces half the mislabeled commits on earth.- One type per commit. If your subject needs the word "and", split the commit. Atomic commits are what make the whole convention pay off.
- Breaking changes need detail. The
!flags it; theBREAKING CHANGE:footer explains what broke and how to migrate. Future-you is the audience.
Frequently asked questions
What's the difference between ! and the BREAKING CHANGE footer?
Both signal a breaking change, and the spec accepts either. The ! after the type or scope is the compact flag; the BREAKING CHANGE: footer is where you explain what broke and how to migrate. Tools like semantic-release watch for both — use the footer whenever there's detail worth writing.
Does the style type mean CSS changes?
No — the most common mix-up. style means code formatting: whitespace, semicolons, lint fixes, nothing that changes logic. An actual CSS change that alters how something looks or behaves is a feat or a fix.
refactor vs chore — which one?
refactor is production code that changed shape without changing behavior — renaming, extracting, restructuring. chore is everything around the code: dependencies, build scripts, tooling, housekeeping. If it touched src/ and changed nothing observable, it's a refactor.
Do I have to use a scope?
No, scope is optional. Use it when it genuinely helps — a module, component, or package name — and keep the vocabulary consistent across the project. feat: add login is fine; feat(auth): add login is better once the project has parts worth naming.
Past tense or present tense?
Imperative present: "add feature", not "added feature" or "adds feature". It matches the style git itself uses for generated messages — "Merge branch", "Apply patch" — and reads as an instruction to the codebase.
Does this work with any git workflow?
Yes — Conventional Commits is just a message convention, not a branching model. It layers onto trunk-based, GitFlow, or anything else. Teams that want enforcement add commitlint with a husky pre-commit hook; the messages themselves are plain git.