Public HTTP versioning with additive-only changes inside a version, explicit breaking-change rules, Sunset headers before removal, and a documented default-version policy that never silently redefines field meaning.
Package status: reference context ready for human review. The contract and test scenarios are complete, but no claim is made that an adopting implementation has passed them.
Decision
Public HTTP APIs identify a served contract by an explicit version name. Inside a named version only additive changes are allowed: optional response fields, new endpoints, and new enum members that old clients may ignore. Changing meaning, type, nullability, or unit of an existing field is breaking and requires a new version name, a changelog entry, and a consumer contract suite. Clients that omit the version header receive the oldest still-supported version, never the newest. Sunset versions keep serving their frozen contract and advertise Sunset and Deprecation headers until removal.
Scope
- Public REST resources listed as public-apis in the adoption profile.
- Version selection, default policy, additive versus breaking classification, sunset signaling, and unknown-version rejection.
Outside this block
- Event payload evolution on message buses (related event-schema package).
- How consumer and provider suites run in CI (related contract-testing package).
Contract
- A request names a served version or, if the header is omitted, executes the pinned oldest-supported version and echoes that name on the response.
- An unknown or retired version name fails closed with a documented code and does not fall through to another schema.
- Additive changes never alter existing field types, units, error codes, or the meaning of null versus absent.
- Breaking changes cannot ship on an existing version name; they require a new served version and dual-running until sunset of the old name.
- Sunset versions include Sunset and Deprecation headers on every response for that version until the sunset instant.
- Field names retired in a new version remain readable under the old version with the old meaning until that version is removed.
Implementation guidance
- Represent version as a serializer choice, not scattered handler branches. Generate provider schemas per version and fail CI when a diff on an existing version is classified breaking.
- Caches must vary on the version header.
Failure handling and safeguards
- Silent reinterpretation of a field under the same name is a defect. If serializers disagree about a version, fail the request rather than mixing two schemas.
- After the sunset instant, that version returns the unknown-version failure.
Verification and operations
- Contract tests pin golden responses per served version and reject unexpected field-meaning diffs. Canary metrics count unknown-version and sunset traffic before removal.
The executable-looking examples in this package are fixtures and acceptance contracts. Run
python tools/validate.py from the collection root to check package structure and metadata; then
implement and execute the scenarios in the target repository.
Adoption assumptions
- Names and numeric values in
example.yamlare an adoption profile, not universal defaults. - The adopting team must map actors, data classes, error vocabulary, and ownership to its system.