Deprecation handling has policy but no tooling
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 15
- Forks
- 3
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 184
Description
"Deprecate, don't break" is documented in three places and enforced by none. Nothing checks that a removal was preceded by a deprecation, that a @deprecated marker comes with a runtime warning, or that a deprecation reaches the release notes. The rule is carried by review attention alone.
Post-1.0.0: nothing here is due at the cut, and the guard belongs with the branch model that starts there.
Found while working through #2883 — the extension-developer upgrade report hit the one case where the policy failed silently.
What exists
- Policy.
docs/release-workflow.md(deprecate, don't break;@deprecatedis fine, removals wait for 2.0.0), ADR 0005 (same rule as the SemVer contract), ADR 0003 (deprecatedderived from a component-level@deprecated, feeding the status registry and the docs callout). - Process.
.claude/templates/release-notes.mdcarries a fixed## Deprecationssection..claude/commands/review-flow.mdasks the reviewer whether a breaking prop change has auseWarnDeprecationpath instead. - Machine-readable. The generated status registry is the only structured artifact, and only the docs site reads it.
The single mechanical gate, commit-guard.yml, routes breaking changes away from main/next. It says nothing about deprecations, and it is dormant until the cut.
Gaps
1. The breaking-change guard was never built. ADR 0003 defers it to #2711 and fixes the registry shape so it is not blocked later — the consumer does not exist. Nothing checks that a symbol or prop that disappeared was deprecated in a published version first. That is the removal half of the contract, and at the 2.0.0 cut it is the half that matters.
2. @deprecated and useWarnDeprecation drift apart. ADR 0003 keeps the warning manual on purpose, so the two are independent by design — but nothing reports a mismatch. Both directions are live today:
Action.actionwarns at runtime with no@deprecatedanywhere, because the type was removed while the runtime fallback stayed (packages/components/src/components/Action/Action.tsx). This is exactly what the extension developers hit: atscerror with nothing in the IDE explaining it, and nothing to grep for.- The reverse — a
@deprecatedprop without auseWarnDeprecation— is invisible to a consumer collecting warnings throughDeprecationWarningProvider, which is the discovery path #2884 documents on the versioning page.
3. No generated deprecation inventory. #2883 item 5. Collected by hand once and already incomplete the day it was written: eight entries listed, thirteen in main. packages/components/dev/status-registry/deriveComponentStatus.ts already parses @deprecated including the migration text, so the inventory is derivable from a source of truth that exists.
4. The release-notes and migration side is unenforced. The ## Deprecations section is a prompt for whoever drafts the notes, not a gate. The migration-note gate is #2883 item 4 and explicitly out of scope in #2891.
Proposed
- Build the guard #2711 deferred. Reads the registry, exempts
betaanddeprecated, fails when a public symbol or prop disappears without a preceding deprecation. - Pair
@deprecatedwith a runtime warning. A check that reports either side standing alone. Cheapest item here, and it would have caught theActioncase before release. - Generate the deprecation inventory as a committed artifact. Closes #2883 item 5 and gives the 2.0.0 cut a scope list that cannot go stale.
- Decide whether
## Deprecationsbecomes a gate, together with the migration-note gate from #2883.
Item 2 closes the reported failure and is a day's work. Item 1 is what the 2.0.0 promise actually rests on.
Why post-1.0.0
A deprecation is additive and may land in any minor, so none of this blocks the cut. The deadline is the 2.0.0 cut: whatever is removed there must have been deprecated in a published 1.x before it, and a warning only counts as lead time if consumers saw it across real releases. The practical rule is to deprecate when the successor lands, not when the removal is planned.
That constraint is what #2774 has to plan against, which makes the inventory (item 3) the most useful piece to have early.
Related: #2883, #2711, #2774, #2884, #2891.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Read docs/release-workflow.md, docs/adr/0003-component-lifecycle-status.md, docs/adr/0005-semver-contract.md, and the existing commit-guard.yml and status-registry/deriveComponentStatus.ts. First determine which of the four proposed items is in scope and how it relates to #2711, #2883, and #2891. Done should include an agreed guard or artifact with defined inputs, failure conditions, and release integration.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend, release, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100