Deprecation and removal policy
ADR-127 says which classes the semver promise covers. This page says how something leaves that surface again.
The rule from 1.0
Nothing marked @api is removed, renamed or narrowed without a
deprecation first:
- The member ships as
@deprecatedin a released minor (1.N.0). - It keeps working, unchanged, through at least one further minor line — deprecated in 1.N.0 means still present and still working in 1.N+1.0.
- Only then may it go, and only in the next major (2.0.0). A minor
release never removes
@api.
"Narrowed" includes a widened constructor: a new required argument on a
class consumers build with new breaks them exactly as a deleted method
would. The API snapshot records constructors for that reason.
During 0.x
The notice period starts at 1.0. While the extension is pre-1.0, minor releases may break with a CHANGELOG entry under a BREAKING heading — see API stability. Deprecations shipped during 0.x are listed below and are the first candidates for removal at 1.0; the ones marked retained are not, and say why.
What a deprecation must carry
@deprecated since X.Y.0 — <what to call instead>in the member's docblock. Thesinceversion is the evidence for the notice period.- A
### Deprecatedentry inCHANGELOG.mdnaming the replacement. - A row in the inventory below.
What enforces what
Half of this policy is a gate and half is a review duty. The difference matters, so it is written down rather than implied.
| Rule | Enforced by | Mechanical |
|---|---|---|
A removed @api member cannot land unnoticed | Tests/Unit/Api/ApiSurfaceSnapshotTest — the rendered surface is
frozen in api-surface.txt and a removal is classified
breaking, which forces an explicit snapshot change in the same
pull request | yes |
| A changed signature — including a widened constructor — cannot land unnoticed | the same test; constructors are part of the rendered surface | yes |
Every @deprecated member of an @api class has a written
migration | Tests/Unit/Api/DeprecationInventoryTest — the member needs a row
between the inventory markers and a non-empty "Use instead" cell.
It reads the docblocks for all five shapes a deprecation takes:
method, constant, public property, enum case and the type itself | yes |
| The inventory cannot keep listing what the code no longer deprecates | the same test, in the other direction | yes |
| The notice period itself — deprecated for at least one minor line before removal | review. Nothing in the repository knows in which release a docblock
tag first appeared; the since version is the only evidence, and
a reviewer has to read it | no |
The ### Deprecated CHANGELOG entry | review. Build/Scripts/check-changelog-unreleased.php refuses a
[Unreleased] section that repeats itself; it does not require any
particular entry to be present | no |
Currently deprecated
Everything @deprecated on an @api class today, with the call that
replaces it. The two directions of this table are asserted against the
docblocks, so it is complete by construction.
| Member | Since | Use instead |
|---|---|---|
Model::getCapabilities() | 0.8.0 | getCapabilitySet() |
Model::getCapabilitiesArray() | 0.8.0 | getCapabilitySet()->toStringList(). The typed list deduplicates
and drops unknown tokens; the legacy accessor preserves both. |
Model::getCapabilitiesAsEnums() | 0.8.0 | getCapabilitySet()->capabilities |
Model::setCapabilities() | 0.8.0 | setCapabilitySet() with a typed set — it validates against the
capability enum and deduplicates. |
Model::setCapabilitiesArray() | 0.8.0 | setCapabilitySet(CapabilitySet::fromArray(...)) |
Model::hasCapability() | 0.8.0 | getCapabilitySet()->has() — accepts both the enum and the legacy
string form. |
Model::addCapability() | 0.8.0 | setCapabilitySet(getCapabilitySet()->with(...)) |
Model::removeCapability() | 0.8.0 | setCapabilitySet(getCapabilitySet()->without(...)) |
Provider::getOptions() | 0.8.0 | getOptionsObject(). Retained — Extbase hydrates the entity
through this getter/setter pair. |
Provider::getOptionsArray() | 0.8.0 | getOptionsObject() |
Provider::setOptions() | 0.8.0 | setOptionsObject(). Retained — Extbase property mapping. |
Provider::setOptionsArray() | 0.8.0 | setOptionsObject() |
LlmConfiguration::SELECTION_MODE_FIXED | — | the ModelSelectionMode enum, case FIXED |
LlmConfiguration::SELECTION_MODE_CRITERIA | — | the ModelSelectionMode enum, case CRITERIA |
LlmConfiguration::getModelSelectionCriteria() | 0.8.0 | getModelSelectionCriteriaDTO(). Retained — Extbase property
mapping. |
LlmConfiguration::setModelSelectionCriteria() | 0.8.0 | setModelSelectionCriteriaDTO(). Retained — Extbase property
mapping. |
LlmConfiguration::getOptions() | 0.8.0 | getOptionsArray(). The options field carries provider-specific
extras, so the typed surface stops at the array. Retained — Extbase
property mapping. |
LlmConfiguration::setOptions() | 0.8.0 | setOptionsArray(). Retained — Extbase property mapping. |
LlmConfiguration::getFallbackChain() | 0.8.0 | getFallbackChainDTO(). Retained — Extbase property mapping. |
LlmConfiguration::setFallbackChain() | 0.8.0 | setFallbackChainDTO(). Retained — Extbase property mapping. |
Retained means the member is deprecated for application code but cannot be
deleted: Extbase hydrates the entity through the raw getter/setter pair, so
removing it would break persistence rather than only callers. Those rows
stay past 1.0 and past 2.0. The two SELECTION_MODE_* constants predate
the since convention; they carry no version because none was recorded,
not because none applies.