Conventional Commits
Status: Complete
Category: Clean Code
Default enforcement: Soft
Author: PushBackLog team
Tags
- Topic: clean-code, process, automation
- Skillset: backend, frontend, fullstack, devops
- Technology: generic
- Stage: execution
Summary
Conventional Commits is a lightweight specification for commit message structure that makes the history of a repository machine-readable and human-scannable. By encoding the type, scope, and breaking-change status of every commit in a structured format, teams can automate changelog generation, trigger semantic version bumps, and enforce a consistent vocabulary for code changes. It adds a negligible overhead to writing commits and yields significant returns in automation and communication.
Rationale
Commit messages are a communication medium
A commit message is documentation addressed to two audiences: the next human who reads the git log, and any automated system that processes changes. Unstructured commit messages like “fix stuff”, “wip”, “update”, or “changes” serve neither audience. A structured format with a consistent vocabulary tells the reader immediately what class of change occurred, where it occurred, and whether it is a breaking change.
Automation requires structure
Semantic release tooling (semantic-release, standard-version, changesets) can automatically determine the next version number and generate a changelog from commit history — but only if commits carry structured metadata. Conventional Commits provides the standard interface between commit history and these tools. A feat: commit triggers a minor version bump; a fix: triggers a patch; a BREAKING CHANGE: footer triggers a major bump.
Guidance
Format
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Types
| Type | When to use |
|---|---|
feat | A new feature visible to users or API consumers |
fix | A bug fix |
docs | Documentation changes only |
style | Formatting, whitespace — no logic change |
refactor | Code change that neither fixes a bug nor adds a feature |
perf | Performance improvements |
test | Adding or correcting tests |
build | Build system, package dependencies |
ci | CI/CD pipeline changes |
chore | Maintenance tasks that don’t fit the above |
revert | Reverts a prior commit |
Examples
feat(auth): add magic-link email login
Users can now authenticate using a passwordless magic link sent to their
registered email address. Falls back to password login if the email fails
to deliver within 5 minutes.
Closes #342
fix(billing): prevent duplicate charge on payment retry
Idempotency key was not being forwarded in the retry handler, causing
the payment gateway to process the charge twice on network timeout.
Fixes #489
feat(api)!: remove deprecated v1 endpoints
BREAKING CHANGE: /v1/users and /v1/orders are removed. Consumers must
migrate to /v2 equivalents. Migration guide: docs/v2-migration.md
Scope
The scope is a noun describing the part of the codebase affected: (auth), (billing), (api), (ci), (deps). Scopes should match the domain boundaries or module names of your codebase. Keep the list consistent — establish agreed scopes in your CONTRIBUTING guide.
Breaking changes
Breaking changes are marked with an exclamation mark after the type/scope (feat!:) or with a BREAKING CHANGE: footer in the commit body. Both trigger a major version bump in semantic release tooling.
Enforcement
Lint commit messages on every commit using commitlint:
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional']
};
Run in CI or via a commit-msg git hook (husky):
# .husky/commit-msg
npx --no -- commitlint --edit $1
Atomic commits and Conventional Commits
Conventional Commits work best when combined with atomic commits — each commit represents a single logical change. A commit that contains three unrelated changes cannot be classified under a single type accurately. The commit discipline enforces the commit structure discipline.