API Versioning and Lifecycle Guide¶
This is the canonical contributor guide for JSON API versioning and lifecycle policy in wagtail-unveil.
Use this document when deciding whether a change requires a new API version and when implementing/deprecating versions.
Purpose And Scope¶
Audience:
- maintainers and contributors changing JSON API behavior
- reviewers validating whether changes are backward-compatible
Scope:
- JSON API contract versioning only (
/unveil/api/vN/...) - lifecycle states and timelines for active/deprecated versions
- implementation workflow for introducing new versions
Out of scope:
- package release versioning (
package_version) - discovery internals (see discovery-architecture.md)
Why Explicit Versioning¶
The package serves API clients that may not upgrade in lockstep with package releases. Explicit API versions let us:
- preserve existing client behavior under stable endpoints
- ship breaking improvements in parallel under a new version
- provide predictable deprecation windows before removal
This policy prioritizes contract stability over "silent replacement" of behavior.
Core Concepts¶
stable: supported default contract; no scheduled removal datedeprecated: still served, but scheduled for retirement with communicated datessunset: date after which the deprecated version may be removedremoved: endpoint/version no longer served
Contract version vs package version:
metadata.api_versionidentifies the API contract (v1,v2, ...)metadata.package_versionidentifies the installedwagtail-unveilrelease
Breaking change (for this project):
- any change that can make a compliant client fail, mis-parse, or change behavior unexpectedly without client code changes
Default Lifecycle Policy¶
Default policy is normative unless an exception is documented.
- Deprecation window: 180 days from deprecation notice date
- A version can be removed only when:
- a successor version is already available, and
- the deprecation window has elapsed
- removal is optional after the window
Exception policy (urgent/security):
- maintainers may shorten the window only with explicit rationale
- rationale must be recorded in release notes/changelog
- replacement guidance for clients must be included in the same release communication
Decision Matrix: Do We Need a New API Version?¶
| Change type | Example | Breaking? | New version required? | Notes |
|---|---|---|---|---|
| Add optional response field | Add metadata.request_id |
No | No | Existing clients can ignore unknown fields. |
| Add optional metadata object | Add metadata.api_lifecycle |
No | No | Must be additive and optional. |
| Rename response field | name -> url_name |
Yes | Yes | Breaks clients expecting old key. |
| Remove response field | Remove skip_reason |
Yes | Yes | Breaks clients relying on field presence. |
| Change field type/format | count int -> string, date format changes |
Yes | Yes | Parser/validation breaks. |
| Change semantics of existing field | source value meaning changes |
Yes | Yes | Behavior changes without schema change are still breaking. |
| Change auth requirements/status semantics | Previously 403 now 401/redirect | Yes | Yes | Client auth/error handling can break. |
| Add new endpoint | Add api/v1/health/ |
No* | No* | *No version bump unless existing contract behavior changes. |
| Reorder arrays where order is meaningful | Stable sorted output -> unsorted | Yes | Yes | Treat order as contract when documented or relied on. |
If unsure, treat the change as breaking and open a versioning review in PR notes.
Version Bump Workflow¶
When a new version is required (for example v2):
- Add contract entry in
wagtail_unveil/api_contract.py - Add
APIVersionContractentry toAPI_VERSION_REGISTRYwith: versionstatusdeprecated_onsunset_on- versioned backend/frontend path and URL names
-
Keep old versions in registry during deprecation window.
-
Confirm dynamic routing in
wagtail_unveil/urls.py - Ensure both backend and frontend routes are generated for new version.
-
Confirm old version routes still resolve.
-
Implement version-specific behavior in
wagtail_unveil/views.py - Extend version dispatch/serializer logic for the new version.
- Keep previous version behavior intact.
-
Ensure metadata includes correct
api_version+api_lifecycle. -
Validate report target behavior
- Reports should point to
get_latest_stable_api_contract()target. -
Confirm this matches desired default version policy.
-
Validate lifecycle headers
- Deprecated versions must emit:
Deprecation: true-
Sunsetheader when sunset date exists. -
Keep previous version live
- Do not remove previous version endpoints until deprecation window has elapsed and communication is complete.
Testing Requirements For New Versions¶
Required coverage when adding/changing versions:
- registry validation and latest stable selection
- route resolution for new version names/paths
- metadata shape and values:
api_versionapi_lifecycle- deprecation header behavior (
Deprecation,Sunset) - report
data-api-urltargets latest stable version - backward compatibility tests for previous version
Verification commands:
make lint
make coverage
Release And Communication Checklist¶
For every version introduction/deprecation update:
- Update:
README.mddocs/index.mdAGENTS.md-
wagtail_unveil/AGENTS.md -
Release notes/changelog:
- deprecation date
- sunset date
- target replacement version
-
migration guidance
-
Migration note template:
### API Deprecation Notice
- Deprecated version: `vN`
- Deprecated on: `YYYY-MM-DD`
- Sunset on: `YYYY-MM-DD`
- Replacement: `vN+1`
#### Required Client Actions
1. Switch endpoint paths from `/unveil/api/vN/...` to `/unveil/api/vN+1/...`.
2. Update response parsing for renamed/removed/changed fields.
3. Verify error/auth handling against `vN+1` semantics.
Worked Examples¶
Example A: Additive Metadata Field (No New Version)¶
Change:
- add
metadata.request_idfor traceability
Decision:
- additive optional field, no change to existing keys/types/semantics
- no new API version required
Implementation:
- update existing version serializer/metadata builder
- add tests confirming old fields unchanged and new field present
Example B: Rename Response Key (New Version Required)¶
Change:
- rename
resolved_routetoresolved_url
Decision:
- key rename is breaking
- new API version required (for example
v2)
Implementation outline:
- Add
v2entry inAPI_VERSION_REGISTRY. - Keep
v1active and markv1asdeprecatedwith dates. - Implement
v2serializer with renamed key. - Keep
v1serializer unchanged. - Add tests for coexistence (
v1old key,v2new key). - Publish deprecation and sunset dates in release notes.