skip to content

In a Conventional Commits message, what do the type, scope, and trailing ! mean?

level: middleimportance: should knowfreq 50%

answer

  1. the subject becomes structured data
  2. only two types are actually specified
  3. parentheses name the affected area
  4. one punctuation mark means incompatible
  5. it drives version bumps and changelogs

basics

~20 s

Conventional Commits shapes the subject as type(scope)!: description. The type classifies the change, the optional scope names the affected area, and a ! before the colon marks a breaking change. It makes commit history machine-readable for versioning and changelogs.

solid answer

~50 s

The format is `<type>[(<scope>)][!]: <description>`, followed by an optional body and footers. The **type** classifies the change — the specification only requires that `feat` introduces a feature and `fix` patches a bug; teams commonly add `docs`, `refactor`, `test`, `perf`, `build`, `ci` and `chore` by convention. The **scope** is a free-form noun in parentheses naming the affected area, such as `feat(parser):`; your team decides the vocabulary. A **`!`** before the colon flags a breaking change, equivalent to a `BREAKING CHANGE: <description>` footer. The point is a subject line a machine can parse: tooling maps `fix` to a patch bump, `feat` to a minor, and a breaking marker to a major, and can group a changelog by type. The cost is that it only works if everyone follows it, so teams back it with a `commit-msg` hook or a CI check.

code

text · 7 lines
text
feat(api)!: return ISO-8601 timestamps everywhere

Epoch millis forced every client to know the unit, and two of
them got it wrong.

BREAKING CHANGE: all timestamp fields are now strings; parse
them as ISO-8601 instead of integers.

go deeper

for a junior

Recall the shape type(scope): description, that feat and fix are the core types, and that a ! before the colon means the change breaks something.

for a middle

Explain that the point is machine-readable history — the semver mapping and generated changelogs — and that Git enforces none of it, so a hook or CI check does.

for a senior

Discuss where enforcement actually pays off, the failure mode of a single mislabelled commit driving a wrong version, and how your merge strategy affects which subjects survive to be parsed.

for a principal

Judge whether the convention is worth its overhead for a given repository — clear value for published, versioned artifacts, questionable for a continuously deployed internal service — and own that decision explicitly.

## The grammar A Conventional Commits message looks like this: ``` feat(parser): support trailing commas in arrays The tokenizer rejected a comma before a closing bracket, which broke round-tripping of configs written by the editor. Refs: PROJ-142 ``` Structurally it is an ordinary Git commit message — subject, blank line, body, footers. The convention only constrains the shape of the subject line and the footer vocabulary. Git itself knows nothing about it; the value comes entirely from tools and humans agreeing to read it. The subject is `<type>[(<scope>)][!]: <description>`. ## Type The type is a lowercase noun classifying the *kind* of change. The specification pins down only two: `feat` for a new feature and `fix` for a bug fix. Everything else — `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore` — is convention inherited from the Angular guidelines and is a team choice, not a rule. The important discipline is that the type describes the change's nature, not the file it touched: editing a test file as part of a bug fix is still `fix`. ## Scope The scope is an optional parenthesised noun naming the part of the codebase affected: `fix(auth):`, `feat(api):`. The specification does not define a vocabulary, so a team picks one — module names, package names, or service names. Two failure modes are common: scopes so fine-grained that they mirror file paths and add nothing, and a free-for-all where the same area appears under three spellings. Both are avoidable with a short agreed list. ## The breaking-change marker A `!` placed immediately before the colon — `feat(api)!: drop the v1 response envelope` — declares that the change breaks consumers. The alternative, and the one that lets you explain yourself, is a footer: a line beginning `BREAKING CHANGE: ` followed by a description of what broke and what to do about it. Using both is fine and common: the `!` is visible in `git log --oneline`, the footer carries the detail. ## Why bother The payoff is that a commit history becomes structured data: - **Version inference.** The specification maps `fix` to a patch release, `feat` to a minor release, and a breaking marker to a major release, in semantic-versioning terms. A release process can compute the next version from the commits since the last tag rather than from someone's memory. - **Changelog generation.** Grouping by type gives "Features" and "Bug Fixes" sections without anyone maintaining a file by hand. - **Human scanning.** `git log --oneline` becomes filterable by eye, and `git log --grep '^fix'` becomes meaningful. ## The costs The convention is only as good as its worst commit. A single `feat:` on a bug fix mislabels a release; a `chore:` on a breaking change silently ships an incompatible version. So teams that rely on the mapping enforce it — typically a `commit-msg` hook that rejects a non-conforming subject, plus a check in CI so the rule survives a developer who has not installed hooks. Enforcement has its own cost: it blocks commits at an annoying moment and tempts people into a habitual `chore: update` that satisfies the parser and tells the reader nothing. There is also a scope mismatch worth naming: the convention classifies commits, but releases are usually cut from merged branches. If your history is squashed or merged in ways that lose individual subjects, the mapping operates on whatever survives, which is a decision your merge strategy makes, not the convention. ## When it is worth adopting It earns its keep where automated versioning or generated changelogs are genuinely wanted — published libraries, versioned APIs, anything with external consumers reading release notes. In an internal application deployed continuously from the mainline, the machine-readability may buy nothing, and the honest answer in an interview is that the convention is a means to an end rather than a virtue in itself. ## Interview framing Give the grammar precisely, name `feat` and `fix` as the only specified types, explain the `!` and the `BREAKING CHANGE:` footer as equivalents, and then say what the structure is *for*. Candidates who recite the type list but cannot say why the type matters have learned the form and missed the function.

  • Which types does the Conventional Commits specification actually define?
    Only `feat` and `fix` — a feature and a bug fix, mapping to a minor and a patch release respectively. Everything else, such as `docs`, `refactor`, `test`, `perf`, `build`, `ci` and `chore`, is a widely copied convention rather than part of the specification. Teams are free to define their own list, which is why the list should be written down somewhere.
  • What is the alternative to the ! marker for declaring a breaking change?
    A footer line beginning `BREAKING CHANGE: ` followed by a description of what broke and how to migrate. It carries detail the subject cannot, while the `!` stays visible in one-line log output. Using both is common: the marker for scanning, the footer for the explanation.
  • How do teams stop non-conforming messages from slipping in?
    A `commit-msg` hook that parses the subject and rejects it locally, backed by a check in the pipeline so the rule still holds for anyone who has not installed hooks. The reason enforcement matters is that the value is all-or-nothing: one mislabelled commit is enough to compute the wrong release version.

saying these in an interview costs you the question

  • Thinks Git itself parses or enforces the format
  • Believes the full type list is part of the specification
  • Uses chore: for changes that break consumers
  • Treats the scope as the file path that changed
  • Assumes adopting the convention alone produces releases

context