Structure for Humans and Machines
If SemVer defines what the next version should be, Conventional Commits defines how to determine it automatically from your Git history. It is a lightweight specification that imposes a consistent structure on commit messages, making them parseable by both humans and tools.
The format is:
<type>(<scope>): <short description>
[optional body]
[optional footer(s)]
The type field is the most important part. It categorizes the intent of the change:
| Type | Meaning | SemVer Impact |
|---|---|---|
fix |
Patches a bug | PATCH |
feat |
Adds a new feature | MINOR |
BREAKING CHANGE (footer) |
Incompatible API change | MAJOR |
chore |
Maintenance, tooling, dependencies | None |
docs |
Documentation only | None |
refactor |
Code restructuring, no behavior change | None |
test |
Adding or correcting tests | None |
perf |
Performance improvement | None (or PATCH) |
ci |
CI/CD configuration changes | None |
build |
Build system changes | None |
The scope field (optional) names the part of the codebase affected. It appears in parentheses after the type:
feat(auth): add OAuth2 login support
fix(api): handle null response from payment gateway
Breaking Changes can be declared in two ways:
# Method 1: Footer (preferred for feat/fix that also break)
feat(db): migrate to new schema format
BREAKING CHANGE: The v1 schema is no longer supported. Run the migration script before upgrading.
# Method 2: Exclamation mark shorthand
feat(db)!: migrate to new schema format
Both methods signal a MAJOR version bump to automated tools.
Real examples of well-formed commits:
fix(auth): prevent session token from expiring prematurely
Tokens were being invalidated after 30 minutes regardless of activity.
Now the expiry window resets on each authenticated request.
feat(payments): add support for SEPA bank transfers
chore(deps): bump lodash from 4.17.20 to 4.17.21
docs(api): add rate limiting section to README
refactor(cache): replace in-memory store with Redis adapter
BREAKING CHANGE: The cache module now requires a Redis connection string
via the REDIS_URL environment variable.
Why this matters beyond automation: Even if you never use an automated release tool, Conventional Commits make your Git log a navigable history. git log --oneline becomes a structured audit trail. Code reviewers can immediately understand the intent of a commit without reading the diff. New team members can understand the project's evolution in minutes.