Writing Good Commit Messages with Conventional Commits

A
Author
25 Aug, 2026 1 min read 178 views

Six months from now, git log and git blame are how someone (often you) will understand why a line of code exists. The commit message is where that answer lives.

The classic format

Fix double charge when payment webhook is retried

The payment provider retries webhooks if we respond slower than
10 seconds. We processed each retry as a new payment. Store the
event id and ignore duplicates.

Refs: #482
  • A short summary line (around 50-72 characters) in the imperative mood: "Fix", "Add", "Remove".
  • A blank line, then a body explaining what and why, not how (the diff shows how).
  • References to issues or tickets at the end.

Conventional Commits

A widely used convention that adds a type (and optional scope) to the summary:

feat(cart): add coupon code support
fix(payments): ignore duplicate webhook events
docs: explain local setup with Docker Compose
refactor(auth): extract token validation into a module
perf(search): cache popular queries for 60 seconds
chore(deps): bump express to 5.1.0

feat(api)!: remove deprecated /v1/orders endpoint

BREAKING CHANGE: clients must use /v2/orders.
  • feat - a new feature (minor version bump).
  • fix - a bug fix (patch version bump).
  • ! or a BREAKING CHANGE: footer - a major version bump.
  • Others: docs, refactor, perf, test, build, ci, chore.

Why teams adopt it

  • Tools like semantic-release and release-please generate version numbers and changelogs from commits.
  • History becomes scannable: filter all fixes in a release with git log --grep "^fix".
  • Reviewers see the intent of each commit at a glance.

Enforce it gently with commitlint in a Git hook or CI, or apply it to pull request titles if you squash-merge.

Was this article helpful?

Comments

No comments yet. Have a question or a tip? Be the first to share it.

Leave a comment

Comments are reviewed before they appear.