Skip to content

Commit Messages and Conventional Commits

Commit Messages and Conventional Commits

Why it matters

Clear commit messages communicate intent and make history searchable. They help reviewers, enable reliable rollbacks, and allow automation (changelogs, release tooling) to work predictably.

The Conventional Commit format (summary)

<type>(optional scope): <short summary>

Common types: feat, fix, docs, chore, refactor, test, perf, build, ci

Expanded guide

Overview

A commit message is a log of your changes. Clear, consistent commit messages communicate the why behind a change, not just the what. The Conventional Commits specification provides a simple, machine-readable format that works well with automated releases and changelog generation.

Format (detailed)

<type>[optional scope]: <short summary>

[optional body]

[optional footer(s)]
  • type: one of the conventional types (feat, fix, docs, chore, etc.)
  • scope: optional, a noun describing the affected area (e.g., auth, api)
  • short summary: imperative, ~50 chars or less
  • body: use to explain the motivation, key details, and trade-offs
  • footer: reference issues (e.g., Fixes #123) or mark breaking changes (BREAKING CHANGE:)

Examples

  • Simple:
feat(auth): add Google OAuth login
  • With scope and short description:
fix(api): handle 404 error for missing project
  • With body and footer:
feat(api): implement rate limiting for public endpoints

Prevent abuse by limiting requests to 100/min per IP. Uses
express-rate-limit and adds configuration to the server startup
to allow overrides in staging/production.

Fixes #42
  • Breaking change example:
feat(auth): upgrade token format

BREAKING CHANGE: token format switched to JWTv2 which is not
backward-compatible with JWTv1. All clients must rotate tokens.

Commit message style tips

  • Use imperative mood in the subject line ("add", not "added" or "adds").
  • Keep the subject under ~50 characters and wrap the body at ~72 characters.
  • Use the body to explain why the change was made and any non-obvious consequences.
  • Avoid including stack traces or long diffs in commit messages; prefer linking to issues.
  • Keep commits focused and small — one logical change per commit.

Tooling & automation

  • Use linters like commitlint to enforce Conventional Commits in CI.
  • Use git hooks (husky, pre-commit) to run linters or prompt for message format locally.
  • Many CI/CD tools can consume Conventional Commit messages to auto-generate changelogs and decide semantic version bumps.

Practical workflows

  • For feature branches, write clear commit messages for each logical step. When ready, squash or rebase locally if you want a tidy history before opening a PR.
  • For collaborative branches, prefer smaller commits and avoid rewriting history once pushed.

1. Why Structure Matters

Benefit Impact
Greppable history Quickly locate introduction of features/bugs
Automated changelogs Release notes generation (semantic-release)
Easier debugging Bisecting & reasoning about intent
Semantic versioning Map commit types to version bumps

2. Type Reference Table

Type Meaning Semantic Version Impact (Typical)
feat New feature MINOR
fix Bug fix PATCH
docs Docs only None
chore Maintenance / tooling None
refactor Internal change no behavior diff None
perf Performance improvement PATCH (or MINOR if notable)
test Add/modify tests None
ci CI pipeline changes None
build Build system / deps None (unless breaking)
BREAKING CHANGE Backwards incompatible MAJOR

3. Bad vs Good Subjects

Bad Issue Improved
"fix stuff" Vague fix(api): handle null user id
"update" No context chore(deps): bump fastapi 0.103→0.104
"adding login form" Not imperative feat(auth): add login form
"Refactored code" Lacks scope refactor(cart): extract price calculator

4. Helpful Body Guidance

Answer these in the body (if not trivial): 1. Motivation (why now?)
2. Approach (key decisions)
3. Alternatives considered (if relevant)
4. Risks / follow-ups
5. Issue links / docs references

Format: wrap lines ~72 chars; blank lines between paragraphs; bullet points for multi-part rationale.


feat(api): change /projects response shape

BREAKING CHANGE: remove legacy field `owner_name`; clients must
use nested `owner.name`.

6. Automation Matrix

Goal Tool Example
Enforce format commitlint + Husky Pre-commit hook rejects bad subject
Auto version + changelog semantic-release Bumps version & publishes notes
Template assistance Git commit template ~/.gitmessage prefilled sections
Sign authenticity GPG / SSH Signing git config commit.gpgsign true

7. Interactive Rebase Polish (Local Only)

git fetch origin
git rebase -i origin/main
# squash/reword as needed
git push --force-with-lease
Do this before requesting review; avoid rewriting once reviewers comment.


8. Quick Checklist

  • Subject < 50 chars, imperative
  • Conventional type present & correct
  • Scope added if clarifying
  • Body explains WHY for non-trivial change
  • BREAKING CHANGE footer if incompatible
  • Issues referenced (Fixes #ID) when applicable
  • No secrets / huge stack traces