ADR-030: API Versioning
Status
Implemented
Date
2025-01-16 (Retrospective)
Decision Makers
- Architecture Team - API evolution strategy
- Frontend Team - Consumer requirements
Layer
API
Related ADRs
- 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:
- Backward Compatibility: Existing clients continue working
- New Features: Ability to add new capabilities
- Deprecation Path: Clear timeline for old versions
- Documentation: Version-specific docs
- 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
- URL Prefix:
/api/v1/,/api/v2/in path - Version Per Router: Separate routers per version
- OpenAPI Per Version: Individual spec files
- Deprecation Headers: Sunset header for deprecated endpoints
- 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
| Version | Status | Support Until |
|---|---|---|
| v1 | Current | Indefinite |
| v0 (if any) | Deprecated | 6 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:
- SRE Operational Fit: URL versioning is ideal for operations teams who frequently use curl/wget for debugging
- Log Visibility: Version appears in nginx access logs, critical during incident investigation
- Code Duplication: Multiple versions can lead to significant code overlap
Required Modifications:
- Shared Core Logic: Extract business logic to version-agnostic services; routers only handle schema translation
- Version Discovery: Add
/api/versionsendpoint listing available versions and their status - Deprecation Metrics: Track usage per version to inform sunset decisions
- OpenAPI Per Version: Ensure distinct OpenAPI specs at
/api/v1/openapi.json
Modifications Applied
- Documented shared service layer pattern
- Added version discovery endpoint recommendation
- 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