Mastering the commit format of type, scope, and subject along with ten commonly used types. Including how to mark a breaking change so that a major release is detected, complete with examples of correct and incorrect commits.

In episode 2 we dissected semantic-release's architecture: the commit analyzer reads commit history and determines the release type. Now we learn the language it reads — because the commit analyzer is only as smart as the commit message format you write. A messy commit is like feeding the machine foreign-language text it can't parse.
Conventional Commits is a lightweight specification that turns commit messages into a parseable format: type(scope): subject. The specification is popular because it's easy for humans to learn and easy for machines to read — exactly the combination release automation needs. No essays required; just one honest line about the kind of change.
The core format is just one line:
type(scope): subject
feat(auth): add login page
fix(validator): fix email validationfeat or fix.auth, validator, api.A good subject answers the question: "what does this commit change?" Visually: feat(auth): add login page means "in the auth part, there's a new feature that is a login page."
| Type | Meaning | Example Subject |
|---|---|---|
feat | New feature | add login page |
fix | Bug fix | fix email validation |
perf | Performance improvement | speed up list rendering |
refactor | Internal change, not a bug | tidy up auth module |
docs | Documentation change | fix setup guide |
test | Add or change tests | add login flow tests |
chore | Routine non-functional task | update security dependencies |
build | Build system change | update bundler config |
ci | CI configuration change | move to setup-node v4 |
revert | Revert a commit | revert commit 3f2a1b0 |
The commit analyzer maps types to release types. With the default preset, only feat and fix trigger a release; the rest need to be configured via releaseRules as this repo does.
| Type | Release Impact |
|---|---|
feat | minor |
fix, hotfix | patch |
perf, refactor, chore, revert | patch |
docs, test, ci, style | no release |
| Breaking change | major |
Note
The default Angular preset only recognizes feat bumping minor and fix bumping patch. Additional mappings like perf and refactor bumping patch, or chore triggering a release, are the result of customizing releaseRules — not default behavior. We'll build our own in episode 5.
Changes that break compatibility are marked with a BREAKING CHANGE: footer or an exclamation mark ! after the type or scope. Both trigger a major release.
feat(auth): migrate to JWT-based tokens
BREAKING CHANGE: the old token format is no longer supported
feat!: drop Node 16 supportImportant note: the BREAKING CHANGE keyword must be in the footer, separated from the subject by a blank line. This repo even registers BREAKING CHANGE and BREAKING CHANGES in parserOpts.noteKeywords so that both forms are recognized.
fix(auth): fix login failing with uppercase email # correct
feat: add dashboard page # correct
update email validation # wrong
fix bug # wrongThe most common mistakes: not using a type at all, or a subject that is too vague like fix bug — what was fixed? where? The machine can't guess, and the resulting changelog will be useless.
Warning
fix bug and update can't be parsed as fix. As a result, that commit doesn't trigger a release, and its fix never reaches users until another correctly formatted commit arrives. A lazy commit message is a hidden cost: the changelog gets corrupted and releases get delayed.
"add login page" # imperative
"adding login page" # gerund, avoid
"add login page." # ends with a period, avoidIn episode 3 you learned:
type(scope): subject format with a required type and optional scope.feat, fix, perf, refactor, docs, test, chore, build, ci, revert.feat minor, fix patch, breaking major.BREAKING CHANGE: footer or the ! exclamation mark.In episode 4 we'll look at where those commits live: the Git Flow branching strategy with staging as the release candidate and main as the stable branch. See you in episode 4!