Conventional Commit types
The prefix that starts a commit message, what each one means, and when to reach for it.
A Conventional Commit starts with a type that says what kind of change this is.
The format is type: short summary, optionally with a scope in parentheses,
like fix(auth): reject expired tokens. The type is the part that tooling reads
to sort changes and bump versions, so picking the right one matters more than it
looks.
The types
| Type | Use it when |
|---|---|
feat | A new feature ships with the change. |
fix | A bug is fixed. |
chore | Something changed that isn't a fix or feature and doesn't touch src or test files, like bumping a dependency. |
refactor | Code was reworked without fixing a bug or adding a feature. |
docs | Only documentation changed, like the README or other markdown. |
style | Formatting only, nothing that changes what the code does: whitespace, missing semicolons, and the like. |
test | Tests were added or corrected. |
perf | A change that makes things faster. |
ci | Continuous integration config or scripts. |
build | The build system or external dependencies. |
revert | Undoes an earlier commit. |
Picking between the close ones
A few types overlap enough to trip you up:
featvsfix: if the behavior was never meant to work that way, it's afix. If it's new ground, it's afeat.refactorvsperf: both leave behavior alone, butperfis specifically for making something faster. Reach forperfonly when speed is the point.chorevsbuild: dependency bumps that don't touch how the app is built arechore. Changes to the build pipeline itself arebuild.stylevsrefactor:stylenever changes meaning, only formatting. The moment logic moves, it'srefactor.
Why bother
The prefix is a promise about scope. A reader scanning git log knows a fix
line is worth a second look and a chore line usually isn't. Tools like
semantic-release use the same signal: feat bumps the minor version, fix
bumps the patch, and a BREAKING CHANGE footer bumps the major. Keep the types
honest and the changelog writes itself.