Conventional Commits: The Complete Guide to Messages Your Future Self Will Thank You For
Every developer has opened a git log that reads like a crime scene: "fix stuff", "wip", "final final v2", "asdf". Six months later, nobody — not even the author — can reconstruct what changed or why. Conventional Commits is the industry's answer to that chaos: a lightweight, human-and-machine-readable convention for commit messages that turns your repository history from a junk drawer into a structured changelog, a release-notes generator, and an archaeological record you can actually search. This guide walks through the full specification, the reasoning behind every rule, and how a conventional commit message generator like the studio above makes the whole thing effortless.
The specification itself is short — version 1.0.0 fits on a single page — and it grew out of the commit conventions pioneered by the Angular team. Its core insight is beautifully simple: if every commit message follows a predictable grammar of type, optional scope, and subject, then tools can do the boring work for you. Want to know every breaking change shipped this quarter? Grep for the ! marker or the BREAKING CHANGE: footer. Want release notes? Group commits by type. Want to automate version bumps? A feat means a minor release, a fix means a patch, and anything breaking means a major. The convention is the contract; the automation is the reward.
Anatomy of a conventional commit
Every conventional commit has up to three parts. The header is mandatory and lives on line one: type(optional scope): subject. A ! immediately before the colon marks a breaking change. The body is optional, free-form, and must begin one blank line after the header — it explains the why, the motivation and context that the subject line cannot fit. The footer is optional metadata that begins one blank line after the body (or after the header if there is no body): issue references like Closes: #42, sign-offs, and the special BREAKING CHANGE: footer. A good git commit message template enforces this skeleton so you never have to think about the punctuation — you just fill in the meaning. The validator above checks each of these rules and reports violations with exact line and column numbers, which is far more useful than a red squiggle with no address.
The subject line deserves special attention because it is the most-read line in all of software. Git tooling — git log --oneline, GitHub's commit lists, notification emails — truncates or displays only the first line, so the subject carries the whole message in hostile display conditions. The convention therefore asks for a short imperative summary: "add OAuth2 login", not "Added OAuth2 login" or "adds oauth2 login." Imperative mood completes the sentence "This commit will…". Keep it under 72 characters so it survives one-line logs, start lowercase, and skip the trailing period. These are style rules rather than spec requirements, which is why CommitCraft reports them as warnings rather than errors — your commit is still valid, just less polished.
The eleven types, explained with examples
The specification defines a fixed vocabulary of types, and learning them is the fastest way to internalize the convention. feat introduces a new feature for the user — feat(search): add fuzzy matching. fix patches a bug — fix(cart): prevent negative quantities. docs touches documentation only; style covers formatting, whitespace, and semicolons — changes that do not alter meaning. refactor restructures code without changing behaviour; perf improves performance; test adds or corrects tests. build affects the build system or dependencies, ci changes CI configuration, chore sweeps up everything else that touches neither source nor tests, and revert undoes a previous commit. That is the complete official set — eleven words that cover virtually all real-world changes.
Choosing between them is usually obvious, but a few pairs cause honest confusion. fix versus refactor: if user-visible behaviour changed, it is a fix (or feat); if only the internals moved, it is a refactor. style versus refactor: style is cosmetic — formatting, naming of locals, lint fixes — while refactor changes structure. chore is the honest bucket for dependency bumps, config tweaks, and release housekeeping; it is not a dumping ground for features you were too lazy to classify. When in doubt, ask which release-notes section you would want the change to appear under — that is the type. The type picker in the studio above pairs each type with its gitmoji and a one-line description, so the choice is always one click, never a memory test.
Scopes: the underrated half of the header
The scope — the parenthesized noun after the type — names the section of the codebase the commit touches: feat(auth), fix(checkout), docs(readme). It is optional, but teams that use it well get a superpower: per-area history. git log --grep="fix(payments)" instantly surfaces every payments bug ever fixed, which is invaluable during incident review. Good scopes are stable nouns that match your architecture — module names, feature areas, or layers — not verbs, not ticket numbers, not the author's name. Keep them lowercase, one word, and consistent: if half the team writes (auth) and the other half writes (authentication), the search superpower evaporates. Agree on a scope list in your contributing guide and treat new scopes as a deliberate decision, not a whim.
Breaking changes without the fear
Breaking changes are where sloppy commit messages cause real damage: a renamed function or a dropped API ships silently, and downstream teams discover it in production. The spec gives you two redundant, grep-friendly signals. The compact one is the ! before the colon — feat(api)!: drop v1 endpoints — visible in every one-line log. The explicit one is the footer: BREAKING CHANGE: the /v1 endpoints now return 410; migrate to /v2, which carries the full migration story. Use both together: the bang for visibility, the footer for explanation. The validator detects either signal and the changelog generator collects every breaking commit into its own highlighted section at the top of the release notes, so no breaking change can hide in a wall of features. That is the difference between a breaking change and a breaking surprise.
Footers: the metadata layer
Footers turn a commit message into structured data. Each footer is a token, a separator, and a value: Closes: #42, Refs: #108, #109, Reviewed-by: Ada, Signed-off-by: Grace <[email protected]>. The spec is precise about the grammar: tokens use hyphens instead of spaces (Acked-by, not Acked by), and the separator is either a colon followed by a space or a space followed by a hash (Refs #42 is also legal). The exception is the BREAKING CHANGE token, which keeps its space and must be uppercase — BREAKING-CHANGE is accepted as a synonym. Footers must come after the body, each separated from it by a blank line, because parsers read the message back-to-front: the last paragraph is footers if every line matches the token pattern. The validator enforces the token grammar, the ordering rule, and non-empty values, catching the classic mistakes — spaced tokens, missing values, footers stranded above the body — before they fossilize into your history.
From commits to changelogs, automatically
Here is where the convention pays for itself. Because every commit declares its type, a changelog is just a group-by query: collect all feat commits under "Features", all fix commits under "Bug Fixes", and so on. Tools like conventional-changelog have done this for years, and the Changelog Forge above brings the same idea to the browser with zero setup — paste the output of git log --oneline (hashes are stripped automatically) or a stack of full messages, and you get clean grouped markdown with breaking changes hoisted to the top. Unparseable lines are not dropped; they land in an "Other Changes" section so nothing is silently lost, and the stats line tells you exactly how many commits parsed cleanly. Generate the draft here, paste it into your release notes, and spend the saved hour on the release itself.
The same structure powers semantic versioning automation. The widely used rule is simple: fix commits bump the patch version, feat commits bump the minor version, and any breaking change — bang or footer — bumps the major version. CI pipelines built on this rule (semantic-release being the most famous) can cut releases, publish packages, and post changelogs with no human in the loop, because the commit messages already contain every decision the pipeline needs. That automation is only trustworthy if the messages are trustworthy, which is exactly why validation matters: one malformed commit can mistarget a release. Validate at compose time — in this studio — and enforce at commit time with hooks, and the pipeline stays honest.
Commitizen and the case for an online alternative
Commitizen is the beloved command-line wizard that prompts you for type, scope, and subject and assembles the message. It is excellent — inside a terminal, on a machine where it is installed and configured. But plenty of real workflows live outside that comfort zone: committing from a web IDE, drafting messages on a phone or tablet, contributing to a repository you have not cloned, teaching the convention in a workshop, or just wanting a zero-install tool that works in seconds. A commitizen alternative online fills exactly that gap: no npm install, no adapter configuration, no Node version drama — open the page, pick the type, write the subject, copy the message. CommitCraft goes further than a prompt wizard by adding the two things Commitizen never had: an offline validator that pinpoints spec violations with line and column numbers, and a changelog generator that turns yesterday's commits into today's release notes. Use whichever fits the moment; the messages they produce are identical, because both speak the same specification.
Enforcing the convention: commitlint, hooks, and CI
Conventions survive on enforcement, not enthusiasm. The standard enforcement stack is commitlint — a linter for commit messages with a conventional-commits ruleset — wired into a commit-msg git hook via husky, so malformed messages are rejected before they are even created. Add the same lint as a CI check on pull requests to catch the messages that slip past local hooks (web commits, squash merges, rebases). Start in warn mode for two weeks so the team learns the rules from the linter's messages rather than from a blocked Friday deploy, then switch violations to errors. Two rules deserve to be errors from day one: the type must be from the known set, and the subject must not be empty — everything else can begin as a warning. Teams that skip enforcement usually discover six months later that "we use conventional commits" means "three of us do, sometimes," and their changelogs show it.
A team rollout playbook
Rolling out the convention to a team is a people problem with a tooling solution. First, agree on your scope vocabulary and write it down — ten to twenty nouns that mirror your architecture. Second, add a git commit message template to the repository (git config commit.template) so every editor opens with the skeleton pre-filled; it is a small nudge with an outsized effect. Third, install the commitlint + husky stack from the previous section, starting in warn mode. Fourth, run a fifteen-minute workshop: live-compose five commits in the studio above, deliberately break the rules, and watch the validator catch each violation with its exact position — seeing the feedback loop once teaches more than reading the spec twice. Fifth, generate your next release notes from the real commit history with the Changelog Forge and show the team the before-and-after; nothing sells a convention like watching an hour of release-notes drudgery collapse into a copy-paste. Finally, add the changelog step to your release checklist so the convention keeps paying rent every single release.
Expect two objections. "This slows me down" fades within a week — picking a type from a grid is faster than agonizing over a free-form summary, and the composer above remembers nothing but asks for everything in order. "Our history is already a mess" is true and irrelevant: adopt the convention from today forward, and consider a one-time pass that rewrites nothing but generates the next changelog from whatever parses. Archaeology improves with every commit; perfection on day one was never the goal. What matters is the direction of the history, and every conventional commit is a small, searchable, machine-readable fact about what your software did and why.
Bottom line
Conventional Commits is one of those rare practices that is simultaneously a communication discipline, a documentation strategy, and an automation substrate. The spec takes an afternoon to learn; the habit takes a week to form; the payoff compounds for the life of the repository. Compose in the studio, validate before you commit, generate your changelogs from the history you actually have — and the next time someone opens your git log, they will find a story instead of a crime scene.