ADR 0012: Announce every removal through one advisory with a published horizon
Status
Accepted
Context
This package’s value is that established calls keep working. Public API compatibility is its first architecture characteristic after artifact integrity. Names are therefore removed slowly: a renamed argument, a retired module, a getter whose service no longer exists.
Four mechanisms for telling the caller something is being removed were written
independently, and only one included a date. A caller could not tell how long
they had, a maintainer could not audit what was due, and the warning category
was a per-author choice – which matters, because downstream projects run their
suites under -W error::DeprecationWarning.
ADR 0005 sets a removal date for legacy NWIS, but it is scoped to that adapter. Nothing recorded the general rule, and the deprecations kept accumulating.
Decision
Every deprecation is announced through the shared mechanism in
dataretrieval._deprecation, and every one has a published removal horizon
recorded in REMOVALS.
A deprecation advisory names three things: what is being removed, what to use instead, and the date on or after which it may be removed. The mechanism accepts an advisory with no date – it then states no date rather than implying a schedule it does not have. A deprecation of a public name is expected to include one, and an advisory naming a replacement the caller cannot yet use is not finished.
REMOVALS is the single table of horizons. One table is auditable – what is
due can be listed, and a horizon can be extended in one place – whereas four
hand-written shims could only be found by grep. A renamed public argument keeps
working under its old name through one shared decorator rather than a shim
written for each getter.
The warning category an advisory uses is not the author’s choice, but the
rule setting it is not this record’s. ADR 0004: Use typed failures and bounded recovery decides
when an advisory is a DeprecationWarning (a name in this package is being
removed) and when it is a DataCurrencyWarning (an upstream dataset has
stopped being updated). This record governs the mechanism and the horizon.
A horizon is a minimum, not a schedule. Passing it permits removal; it does not require one, and removal remains a deliberate change with its own release note.
Consequences
A caller can see, from the warning alone, how long they have and what to migrate to.
Horizons can be audited and extended centrally, so a removal date cannot pass unnoticed in a module nobody is reading.
Deprecating something costs more than adding a
warnings.warncall: the replacement must exist and a date must be chosen. That is the intended cost.The package accumulates long-lived compatibility shims. This is accepted – it is the cost of the compatibility characteristic, and the table makes the accumulation visible rather than hidden.
Nothing is removed on the horizon alone. A removal still needs a release that states it.
Compliance
tests/deprecation_test.py covers the shared mechanism: that an advisory
names its replacement, and that the horizon it prints is the one in
REMOVALS. Per-surface tests assert the individual advisories, including that
a renamed argument still works under its old name. The warning category
assertions – that DataCurrencyWarning is not a DeprecationWarning
subclass – belong to ADR 0004: Use typed failures and bounded recovery.
Notes
ADR 0005 remains the record for legacy NWIS specifically, including its 2027-05-06 date. This record generalizes the mechanism without changing that decision.