describe-changes / references/classification-guide.md
Describe changes — classification guide
A reference shipped with the skill · 1,046 words
Describe changes — classification guide
Seven kinds, one per change. The set is deliberately small: a taxonomy people can hold in their heads gets applied consistently, and a consistent wrong-ish label sorts better than an inconsistent precise one.
The seven
| Kind | The change… | Test |
|---|---|---|
feature |
adds a capability that did not exist | Can a user now do something they could not before? |
bug_fix |
makes existing behaviour match its intent | Was the previous behaviour wrong, not merely worse? |
improvement |
makes existing, correct behaviour better | Speed, ergonomics, clarity, reliability, with no new capability. |
security |
closes an exposure | Does the diff or an advisory name an exploitable condition? |
ops |
changes how the system runs, not what it does | Build, deploy, config, monitoring, dependencies, infrastructure. |
docs |
changes documentation only | Would the compiled or deployed behaviour be byte-identical? |
breaking |
requires callers or users to change | Does something that worked before now fail without action? |
Decision order
Overlaps are common. Resolve them by taking the first kind that applies, in this order:
breaking— it wins over everything. A breaking change is a breaking change whether it arrived as a feature, a fix, or a cleanup, because the reader needs the warning more than the category.security— it wins overbug_fix, because a reader deciding whether to upgrade urgently is asking a different question.bug_fix— wrong behaviour becoming right.feature— new capability.improvement— better, same capability.ops— the system runs differently, users notice nothing.docs— nothing else changed.
Two of these deserve stating plainly:
breakingfirst means a removed API endpoint isbreaking, notops, even though the diff is deletion.docslast means a change that touches documentation and code is neverdocs. Only a documentation-only change is.
Edge cases
A bug fix that adds a flag
The fix is correct but risky, so it ships behind a flag that defaults off.
- Flag defaults off: classify
improvement, and the description says the fix exists and is not yet active. It is not abug_fixfor users, because for users nothing changed. - Flag defaults on:
bug_fix. The flag is an escape hatch, not the change. - Either way, the detail register names the flag and its default. A reader who cannot tell whether the fix is on has learned nothing.
A pure performance change
improvement, unless behaviour changed.
- With a measurement in the change — a benchmark, a recorded before and after — the description may state the gain, and cites it.
- Without a measurement, describe the mechanism the diff shows ("results are now cached for the duration of a request") and do not attach a number. An invented percentage is the single most common false claim in generated release notes.
- If the optimisation changed a result, an ordering, or a rounding, it is a
breakingchange wearing a performance costume. Check for that specifically.
A revert
Classified by what the revert does now, not by what was reverted.
- Reverting a broken change restores correct behaviour:
bug_fix. - Reverting a feature removes a capability users had:
breakingif it was released,improvementif it never reached them. - The title says "Revert", names the original, and the detail register states whether the underlying problem is still open. It usually is.
A dependency bump
- Routine version bump:
ops. - Bump that resolves a named advisory:
security, and cite the advisory identifier. - Bump that forces callers to change:
breaking. - A lockfile-only change with no version movement:
ops, and the description says it is a lockfile refresh rather than implying an upgrade.
A refactor
improvement when it changes nothing observable, and the medium register should
be honest that users will notice nothing. A refactor that fixes a bug on the way
is bug_fix; a refactor that changes a public signature is breaking.
Resist the temptation to describe a refactor as a feature because the diff is large. Size is not significance.
A new configuration option
- Option is optional and defaults to the previous behaviour:
feature. - Option is required and has no default:
breaking. - Option only affects deployment, not product behaviour:
ops.
A migration or schema change
Classify by effect, not by file type.
- Adds a nullable column nothing reads yet:
ops. - Backs a user-visible capability in the same change:
feature. - Drops or renames something callers use:
breaking. - Backfills or corrects wrong stored data:
bug_fix.
Test-only changes
ops. They change how the system is verified, not what it does. A change that
adds tests and the fix they cover is a bug_fix — the tests are evidence, not
the change.
Generated or vendored churn
Classify by the reason for regenerating. A regenerated API client because the API
gained an endpoint is feature or ops depending on whether anything calls the
new endpoint yet. Never classify by the size of the generated diff.
When two kinds genuinely apply
That is a signal about shape, not about classification. A pull request that fixes a bug and adds an unrelated capability should be described as two items in the itemized shape, each with its own kind. Forcing it into one digest means one of the two facts is lost, and it will be the one the reader needed.
If the itemized shape is not available to you, choose by the decision order and state the second fact explicitly in the detail register. Do not let it disappear.
What none of the kinds mean
improvementis not a bin for "unclear". If the kind is unclear, the change is not yet understood; read more of the diff.opsis not "backend". A backend change users can observe is afeature,bug_fix, orimprovement.securityis not "hardening". Adding a header, tightening a validator, or rotating a value on schedule isimprovementoropsunless it closes an exposure that existed. Reserve the label so it retains its urgency.