Skip to content

Commit 3a18715

Browse files
ADR with a proposed versioning strategy for the API
2 parents 80f2c84 + b4d9200 commit 3a18715

2 files changed

Lines changed: 69 additions & 0 deletions

File tree

.github/pull_request_template.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@
1313
## Review checklist
1414

1515
- [ ] Check database queries are correctly scoped to current_provider
16+
- [ ] If this changes the gateway API (`/api/v1/`), confirm whether it is a breaking change — if so, a new major version (`/api/v2/`) is required (see [ADR-006](../docs/adr/ADR-006-Gateway_API_versioning_strategy.md))
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# ADR-006: Gateway API versioning strategy
2+
3+
## Context
4+
5+
The Manage service exposes an HTTP API used by the gateway. The gateway sends DICOM image data and failure reports to Manage via this API.
6+
7+
The API is currently at v1, mounted at `/api/v1/`. As the system evolves, API changes will be necessary. Because the gateway is deployed on-premises at individual hospital sites, Manage and gateway deployments will not always be synchronised. The API therefore needs a versioning strategy that allows both sides to evolve without requiring simultaneous deployment.
8+
9+
## Decision
10+
11+
### Assumptions
12+
13+
- The gateway is the only consumer of this API.
14+
- There is a single gateway codebase, but many deployed instances across hospital sites.
15+
- Hospital IT teams may control when gateway updates are applied, meaning deployed instances may lag behind the latest release.
16+
- Django Ninja is used to implement the API, which supports multiple `NinjaAPI` instances mounted at different URL prefixes.
17+
18+
### Drivers
19+
20+
- Manage and gateway deployments must be partially decoupled.
21+
- The API contract should be clearly defined and versioned so that breaking changes can be introduced safely.
22+
- Complexity should be kept proportionate to actual need — over-engineering versioning infrastructure for a single consumer codebase is wasteful.
23+
24+
### Options
25+
26+
#### Option 1: URL-based major versioning only (chosen)
27+
28+
Use a URL prefix for the major version (`/api/v1/`, `/api/v2/`):
29+
30+
- **Non-breaking changes** (new endpoints, new optional request parameters, new response fields): no version change required. The URL does not change.
31+
- **Breaking changes** (removed or renamed fields, changed behaviour, new required parameters): increment the major version and mount a new `NinjaAPI` instance at the new URL prefix (e.g. `/api/v2/`). The previous major version URL is kept alive for a deprecation period (broadly speaking, until it becomes annoying and pointless to maintain it).
32+
33+
The `NinjaAPI(version=...)` string in `core/api.py` is not used as a manually maintained minor version. Deployed version identity is provided by the commit SHA.
34+
35+
#### Option 2: URL-based major versioning with major.minor convention
36+
37+
As Option 1, but also maintain a `major.minor` version string in the `NinjaAPI` constructor, incremented on every non-breaking change. This was considered but rejected: the minor version string is purely informational, carries no enforcement mechanism beyond code review, and in practice could be forgotten and drift out of sync. The commit SHA already provides reliable deployed-version identity.
38+
39+
#### Option 3: URL-based major versioning with pinned minor version URLs
40+
41+
As Option 2, but also expose pinned minor version URLs (e.g. `/api/v1.1/`) so consumers can opt in to a specific minor version. Given there is a single consumer codebase, this adds routing and documentation complexity with no practical benefit.
42+
43+
#### Option 4: Header-based versioning (`Accept` or custom header)
44+
45+
Version communicated via a request header rather than the URL. Less discoverable, harder to test manually, and not the established convention in NHS Digital services.
46+
47+
### Outcome
48+
49+
Option 1. URL-based major versioning only. This is a reversible decision — if the need for pinned minor version URLs arises (e.g. a second consumer is added), Option 3 can be adopted as a straightforward extension.
50+
51+
### Rationale
52+
53+
The URL is the natural and conventional place for an API version. The major version URL prefix is the only signal that matters for interoperability: it forces an explicit, reviewable decision when a breaking change is introduced. A manually maintained minor version string provides no enforcement and would drift; the commit SHA is a more reliable source of truth for "what is deployed".
54+
55+
## Consequences
56+
57+
- When a breaking change is made to the API, a new `NinjaAPI` instance must be created in `core/api.py` and mounted at the new version prefix in `core/urls.py`. The previous version must be kept alive until all gateway instances in the field have been updated.
58+
- The deprecation period for an old major version is determined operationally: it should remain available until confidence is high that all deployed gateway instances have been updated. Given hospital IT teams may control update schedules, this window may be longer than a typical SaaS deployment.
59+
- If a second API consumer is introduced in future, the pinned minor version URL approach (Option 3) should be revisited.
60+
61+
## Compliance
62+
63+
Breaking API changes must be accompanied by a major version bump (new URL prefix). Code review is the primary enforcement mechanism.
64+
65+
## Notes
66+
67+
- Current API: `v1`, mounted at `/api/v1/` in `manage_breast_screening/core/urls.py`.
68+
- The `NinjaAPI(version=...)` string in `manage_breast_screening/core/api.py` does not need to be maintained as a minor version.

0 commit comments

Comments
 (0)