Skip to main content

ADR-030: API Versioning

Status

Implemented

Date

2025-01-16 (Retrospective)

Decision Makers

  • Architecture Team - API evolution strategy
  • Frontend Team - Consumer requirements

Layer

API

  • ADR-004: RESTful API Design
  • ADR-008: FastAPI with Pydantic

Supersedes

None

Depends On

  • ADR-004: RESTful API Design

Context

APIs must evolve without breaking existing clients:

  1. Backward Compatibility: Existing clients continue working
  2. New Features: Ability to add new capabilities
  3. Deprecation Path: Clear timeline for old versions
  4. Documentation: Version-specific docs
  5. Parallel Support: Multiple versions simultaneously

Requirements:

  • URL-based versioning for clarity
  • OpenAPI spec per version
  • Migration guides between versions
  • Minimum 6-month deprecation notice

Decision

We adopt URL-based API versioning with /api/v{n}/ prefix:

Key Design Decisions

  1. URL Prefix: /api/v1/, /api/v2/ in path
  2. Version Per Router: Separate routers per version
  3. OpenAPI Per Version: Individual spec files
  4. Deprecation Headers: Sunset header for deprecated endpoints
  5. v1 as Current: v1 is the current stable version

URL Structure

/api/v1/requirements          # Current version
/api/v2/requirements # Future version (when needed)
/api/health # Unversioned health check
/docs # OpenAPI UI (default to latest)
/openapi.json # OpenAPI spec (default to latest)

Router Organization

# backend/api/v1/__init__.py
from fastapi import APIRouter

v1_router = APIRouter(prefix="/api/v1")

# Include all v1 endpoints
v1_router.include_router(requirements_router)
v1_router.include_router(capabilities_router)
# ...

# main.py
app.include_router(v1_router)

Deprecation Pattern

@router.get("/old-endpoint",
deprecated=True,
responses={
200: {"description": "Success"},
"headers": {
"Sunset": {"description": "Deprecation date"}
}
})
async def old_endpoint():
"""Deprecated: Use /new-endpoint instead."""
response = await new_endpoint()
response.headers["Sunset"] = "Sat, 01 Jul 2025 00:00:00 GMT"
return response

Version Policy

VersionStatusSupport Until
v1CurrentIndefinite
v0 (if any)Deprecated6 months after v1 GA

Consequences

Positive

  • Clear Versioning: Version obvious in URL
  • Independent Evolution: v2 can break v1 compatibility
  • Easy Routing: nginx/ingress can route by version
  • Documentation: Version-specific OpenAPI specs
  • Testing: Parallel testing of versions

Negative

  • Code Duplication: Some overlap between versions
  • Maintenance Burden: Multiple versions to maintain
  • Client Updates: Clients must update URLs
  • Routing Complexity: More routes to manage

Neutral

  • Version Proliferation: Resist creating too many versions
  • Semantic Versioning: Major versions only in URL

Alternatives Considered

1. Header-Based Versioning

  • Approach: Accept: application/vnd.ops.v1+json
  • Rejected: Hidden, harder to debug, cache issues

2. Query Parameter

  • Approach: /api/requirements?version=1
  • Rejected: Easy to miss, not RESTful

3. Subdomain

  • Approach: v1.api.example.com
  • Rejected: DNS complexity, certificate management

Implementation Status

  • Core implementation complete
  • Tests written and passing
  • Documentation updated
  • Migration/upgrade path defined
  • Monitoring/observability in place

Implementation Details

  • v1 Router: backend/api/v1/
  • v2 Placeholder: backend/api/v2/ (future)
  • OpenAPI: /docs, /redoc
  • Deprecation: Sunset header support

LLM Council Review

Review Date: 2025-01-16 Confidence Level: High (90%) Verdict: APPROVED

Quality Metrics

  • Consensus Strength Score (CSS): 0.88
  • Deliberation Depth Index (DDI): 0.82

Council Feedback Summary

URL-based versioning is the correct choice for an SRE Operations Platform. The visibility in URLs enables "curl-ability" for ad-hoc scripting and version visibility in access logs during incident response.

Key Concerns Identified:

  1. SRE Operational Fit: URL versioning is ideal for operations teams who frequently use curl/wget for debugging
  2. Log Visibility: Version appears in nginx access logs, critical during incident investigation
  3. Code Duplication: Multiple versions can lead to significant code overlap

Required Modifications:

  1. Shared Core Logic: Extract business logic to version-agnostic services; routers only handle schema translation
  2. Version Discovery: Add /api/versions endpoint listing available versions and their status
  3. Deprecation Metrics: Track usage per version to inform sunset decisions
  4. OpenAPI Per Version: Ensure distinct OpenAPI specs at /api/v1/openapi.json

Modifications Applied

  1. Documented shared service layer pattern
  2. Added version discovery endpoint recommendation
  3. Documented deprecation metrics tracking

Council Ranking

  • gemini-3-pro: Best Response (SRE context)
  • gpt-5.2: Strong (operational focus)
  • claude-opus-4.5: Good (RFC compliance)

References


ADR-030 | API Layer | Implemented