api-versioning
$npx skills add blunotech-dev/agents --skill api-versioningImplement API versioning with URL or header strategies, deprecation handling, and backward compatibility. Use when the user asks about versioning, managing breaking changes, evolving APIs, supporting multiple versions, or handling v1/v2 migrations.
| name | description | category |
|---|---|---|
| api-versioning | Implement API versioning with URL or header strategies, deprecation handling, and backward compatibility. Use when the user asks about versioning, managing breaking changes, evolving APIs, supporting multiple versions, or handling v1/v2 migrations. | Backend |
API Versioning Skill
Phase 1 — Discovery
Ask only what isn't obvious from context:
- Strategy already chosen? URL prefix (
/v1/) vs. header (Accept: application/vnd.api+json;version=2) vs. query param — each has non-obvious tradeoffs (see below). - Who are the clients? Internal services (fast migration possible), third-party developers (long deprecation windows), or SDKs you own (can force upgrades). This dictates sunset timelines.
- Existing traffic? If v1 is live, ask for any analytics on usage before deprecating. Never deprecate blind.
- Monolith or microservices? Microservices complicate versioning — each service may be on a different version cycle.
Phase 2 — Strategy Selection
Non-obvious tradeoffs
URL versioning (/v1/users)
- Easiest to route, log, and cache. Default choice for public APIs.
- Trap: teams version the entire API when only one resource changed. Version at the resource level if changes are isolated (
/v1/users,/v2/orderssimultaneously is valid).
Header versioning (API-Version: 2 or Accept negotiation)
- Keeps URLs clean. Preferred for internal/RPC-style APIs.
- Trap: CDN and proxy caches often ignore custom headers. You must add
Vary: API-Versionor caching will silently serve wrong versions. - Trap: Harder to test in a browser or with curl without extra flags. Raises support burden for third-party devs.
Query param versioning (?version=2)
- Only acceptable for read-only, public, non-cacheable endpoints (e.g., a public data API).
- Never use for mutation endpoints — params get dropped in logs, proxies, and form submissions.
Phase 3 — Implementation
Backward compatibility rules (non-obvious)
These are breaking changes most teams miss:
- Adding a required field to a request — breaking. Always optional with a default.
- Narrowing an existing field's type — breaking (e.g.,
idwasstring | number, nowstringonly). - Changing error codes or error shapes — breaking if clients pattern-match on them.
- Reordering enum values — breaking if clients use positional index.
- Removing a field from a response — breaking. Deprecate first; return
nullfor one full version cycle before removing. - Changing pagination shape (
page/limit→ cursor-based) — breaking even if field names stay the same. - Tightening validation — breaking. If you previously accepted
age: -1, rejecting it now is a breaking change for clients that stored and re-sent that value.
Safe non-breaking changes:
- Adding optional request fields
- Adding new fields to responses (clients should ignore unknown fields — enforce this in SDK design)
- Adding new enum values (warn clients to handle unknown enums gracefully)
- Relaxing validation
Deprecation headers (exact spec)
Use standard headers so tooling (Postman, API gateways) picks them up automatically:
Deprecation: true
Sunset: Sat, 31 Dec 2025 23:59:59 GMT
Link: <https://api.example.com/v2/users>; rel="successor-version"
Deprecationheader: RFC 8594 standard. Usetrueor an ISO date for when deprecation started.Sunsetheader: RFC 8594. Exact datetime in HTTP-date format. Must be honored — if you miss the sunset date, clients lose trust permanently.Linkrel successor: Gives clients a machine-readable migration target. Include on every deprecated response, not just docs.
Add these at the router/middleware level, not per-handler. One missed handler means silent deprecation.
Version routing patterns
Gateway-level routing (preferred): Route at the API gateway or reverse proxy. Application code never sees the version prefix.
/v1/* → service:8001
/v2/* → service:8002 (or same service, different handler namespace)
Trap: Don't run two full copies of your service per version unless truly necessary. Instead, use a version flag in the request context and branch at the handler level for changed behavior only.
Shared vs. forked handlers:
- If < 20% of endpoints changed between versions: shared codebase with version branching
- If > 50% changed: fork the handler layer, share the service/data layer
- Never fork the database layer — version in the API, not the schema
Sunset execution checklist
- Add deprecation headers to all v_old endpoints (at least 3 months before sunset for third-party APIs; 2 weeks for internal).
- Log every request hitting deprecated endpoints — export to a dashboard. Track unique client IDs/IPs still on old version.
- Reach out directly to top-10 clients by traffic before sunset.
- Return
410 Gone(not404) after sunset. Include aLinkheader pointing to the migration guide. - Keep the route registered as a
410tombstone for 6+ months. Removing it entirely causes cryptic errors for clients that cached DNS or hardcoded the path.
Version negotiation (if using header versioning)
Always define a default version behavior — what happens when no version header is sent:
- Return latest — Bad. Silently breaks old clients as you release new versions.
- Return oldest stable — Safer default. Document it explicitly.
- Return 400 with a clear error — Best for internal APIs. Forces explicit versioning from day one.
{
"error": "missing_version_header",
"message": "Include 'API-Version' header. Current supported versions: [1, 2]. Default is not assumed.",
"docs": "https://docs.example.com/versioning"
}
Phase 4 — Output
Produce whichever of these the user needs:
- Middleware snippet — deprecation header injection (Express, FastAPI, or Go chi pattern)
- Version routing config — nginx, Kong, or AWS API Gateway route rules
- Breaking change audit — diff two OpenAPI specs and flag breaking changes
- Deprecation notice template — email/changelog copy for notifying API consumers
- Migration guide skeleton — v1 → v2 endpoint mapping table with change descriptions
Always include:
- The exact sunset date in HTTP-date format if generating headers
- A
410handler stub if the sunset has passed or is imminent Varyheader reminder if header versioning is chosen