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 therefore leave slowly: a renamed argument, a retired module, a getter whose service no longer exists.

Four spellings of “tell the caller something is going away” grew up independently, and only one carried 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 going away, what to use instead, and the date on or after which it may be removed. The mechanism tolerates an advisory with no date – it then promises nothing specific rather than implying a schedule it does not have. A deprecation of a public name is expected to carry 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-rolled 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 carries 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 going away) 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 floor, 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 move to.

  • Horizons can be audited and extended centrally, so a removal date cannot arrive unnoticed in a module nobody is reading.

  • Deprecating something costs more than adding a warnings.warn call: 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 what the compatibility characteristic buys, and the table makes the accumulation visible rather than hidden.

  • Nothing is removed on the horizon alone. A removal still needs a release that says so.

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.