Learning lens
Start with judgment, not a universal prescription.
This page is a learning model for understanding the capability. It does not claim exact production internals, exact costs, or an infinite-scale architecture.
Evidence
Conceptual pattern
0->1 default
Start with a durable queue, clear ownership, retries, and idempotency before building a larger event platform.
Cost drivers
Watch for breakpoints
- -backlog grows during normal traffic
- -manual recovery becomes frequent
- -independent consumers need replay
Learn first
Concepts behind this architecture
API Gateway
A controlled front door for authentication, routing, rate limits, and API policy.
Rate Limiting
Control request volume so shared systems stay fair, stable, and protected.
Distributed Tracing
Follow one request across services, queues, databases, and external calls.
JWT
A signed JSON token that carries claims between systems.
The Problem
APIs are contracts with external consumers. Once published, changing a response shape, removing a field, or altering behavior can break thousands of integrations. Yet APIs must evolve — new features, performance improvements, and security fixes require changes. API versioning lets you introduce breaking changes without disrupting existing clients by maintaining multiple versions simultaneously. The challenge is choosing a versioning strategy, managing the lifecycle of each version, minimizing code duplication across versions, and eventually sunsetting old versions gracefully.
Architectural Approaches
URL Path Versioning
Include the version in the URL path: /api/v1/users, /api/v2/users. The most visible and widely used approach. Each major version is a distinct URL namespace.
Pros
- +Most explicit and visible — version is obvious in every request
- +Easy to route at the load balancer or gateway level
- +Simple to cache (different URLs = different cache entries)
- +Easy to document and test each version independently
Cons
- −URL changes break bookmarks and hardcoded links
- −Can lead to URL proliferation (/v1, /v2, /v3...)
- −Philosophically impure — the version isn't part of the resource identity
- −Harder to share common logic across versions without duplication
Header-Based Versioning
Version is specified in a custom HTTP header: X-API-Version: 2 or using the Accept header with content negotiation: Accept: application/vnd.myapi.v2+json. URLs remain stable across versions.
Pros
- +Clean URLs — resource identity doesn't change across versions
- +More RESTful — version is metadata, not part of the resource
- +Can default to latest version if no header is provided
- +Supports gradual migration — clients opt into new versions
Cons
- −Version is hidden — harder to discover and debug
- −Requires custom header handling in every client
- −Load balancer routing based on headers is more complex
- −Caching is trickier — must use Vary header
Query Parameter Versioning
Version as a query parameter: /api/users?version=2. Simple to implement and test. Less common in production APIs but useful for internal services.
Pros
- +Easy to test — just change the query parameter
- +No URL structure changes
- +Simple to implement — just read a query param
- +Works well for internal APIs and GraphQL
Cons
- −Pollutes the query string with non-resource concerns
- −Easy to forget — defaults can be surprising
- −Caching must include the version parameter in the cache key
- −Less conventional for public APIs
Architecture
Key Components
Version Router
Extracts the requested API version from the URL path, header, or query parameter. Routes the request to the appropriate version handler. Returns an error if the requested version is unsupported or has been sunset. Adds version info to response headers.
Version Controllers
Thin adapter layers that translate between the versioned API contract and the shared business logic. v1 controller maps to/from the v1 request/response format, v2 controller maps to/from v2 format. Both call the same underlying service layer.
Response Transformer
Transforms the internal data representation to the version-specific response format. Can add/remove fields, rename keys, change data types, and restructure nested objects. Implemented as middleware or decorators on the response path.
Deprecation Manager
Tracks version lifecycle: active, deprecated, sunset. Adds Deprecation and Sunset headers to responses for deprecated versions. Sends notifications to API consumers using deprecated versions. Enforces sunset by returning 410 Gone after the sunset date.
API Documentation Generator
Generates version-specific API documentation (OpenAPI/Swagger). Each version has its own spec file. Shows deprecation notices and migration guides for deprecated versions. Auto-generated from code annotations or schema definitions.
Version Usage Metrics
Tracks API call volume per version, per client. Identifies which clients are still using deprecated versions. Provides data for sunset decisions — don’t sunset a version with significant traffic without reaching out to affected clients.
Data Model
Additive Changes vs Breaking Changes
Not every change requires a new API version. Understand what’s breaking and what’s not.
Non-breaking (additive) changes — safe to deploy without versioning:
- Adding a new optional field to the response
- Adding a new optional query parameter
- Adding a new endpoint
- Adding a new enum value (if clients handle unknown values)
- Increasing rate limits
- Adding new webhook event types
Breaking changes — require a new version:
- Removing or renaming a field in the response
- Changing a field’s data type (string → number)
- Making a previously optional parameter required
- Changing the URL structure
- Removing an endpoint
- Changing error response format
- Changing authentication mechanism
- Altering the semantics of an existing field
Best practice: Design APIs to be as additive as possible. Use nullable fields, optional parameters, and extensible enums from day one. This minimizes the frequency of breaking changes and the need for new major versions. Many successful APIs (Stripe, Twilio) have maintained backward compatibility within a single version for years through careful additive evolution.
The Stripe Date-Based Versioning Model
Stripe uses a unique versioning approach that’s become an industry model.
How it works:
- Each API key is pinned to the version that was current when the key was created (e.g., 2024-01-15)
- Breaking changes are released as new dated versions (2024-06-01)
- Clients can override their pinned version per-request with a header:
Stripe-Version: 2024-06-01 - All changes between versions are documented with exact diffs
Implementation: Internally, Stripe maintains a chain of version transformers. The latest code always runs, and a series of backward-compatibility layers transform the response to match the requested version. When a client requests version 2024-01-15, the response passes through transformers for every version between 2024-01-15 and the current version — each transformer reverting one breaking change.
Benefits:
- No URL changes — ever
- Clients upgrade on their own schedule
- Each breaking change is individually documented and revertible
- New integrations automatically get the latest version
Drawbacks:
- Complex transformer chain for old versions (technical debt)
- Testing matrix grows with each version
- Must maintain backward compatibility layers indefinitely (or until sunset)
Version Sunset Strategy
Old versions can’t live forever — they accumulate technical debt and security risk.
Sunset timeline:
- Announce deprecation: 6-12 months before sunset. Add
Deprecationheader to all responses. Send email to all API consumers using the deprecated version. - Migration period: Provide a detailed migration guide with before/after examples. Offer office hours or support for complex migrations.
- Warning escalation: 3 months before sunset, start returning
Warningheaders. Log deprecated version usage and reach out to top consumers. - Sunset: Return
410 Gonefor all requests to the sunset version. Include the migration guide URL in the error response body.
Sunset headers (RFC 8594):
Deprecation: Sat, 01 Jan 2025 00:00:00 GMTSunset: Sat, 01 Jul 2025 00:00:00 GMTLink: </docs/migration-v1-v2>; rel="deprecation"
Decision criteria for sunset:
- Less than 1% of total API traffic on the old version
- All major consumers have migrated (verify via usage metrics)
- Security vulnerabilities in the old version that can’t be patched
- Maintaining the old version blocks a critical feature or infrastructure change
Never sunset without notice: Even if traffic is near-zero, always announce. Someone’s production integration might only run monthly.
Real-World Examples
Stripe
Date-based versioning (e.g., 2024-06-01) with per-API-key pinning. Version override via Stripe-Version header. Backward-compatibility transformer chain. Extensive changelog documenting every change between versions.
GitHub
Date-based versioning for the REST API (2022-11-28). URL path versioning for GraphQL (/graphql). Deprecation notices in response headers. Preview features available via Accept header media type.
Twilio
Date-based URL path versioning (/2010-04-01/Accounts/…). Extremely long support windows — the 2010 API is still functional. New dated versions introduce breaking changes while old versions remain stable.
Google Cloud
Major version in the URL path (/v1, /v2). Follows a formal version lifecycle: preview → GA → deprecated → shutdown. Minimum 1-year deprecation notice for GA APIs. Version policy documented in the API Improvement Proposals (AIPs).
Architectural Tradeoffs
URL path vs header-based versioning
Pros
- +URL: explicit, cacheable, easy routing at the gateway
- +Header: clean URLs, more RESTful, version is metadata
- +URL: most widely understood, best developer experience
Cons
- −URL: changes resource identity, URL proliferation
- −Header: hidden, harder to debug, requires Vary for caching
- −Header: clients must set custom headers, easy to forget
Date-based vs integer-based version numbers
Pros
- +Date: clear temporal context, natural for changelogs
- +Integer: simpler, easier to compare (v2 > v1)
- +Date: can have many micro-versions without confusion
Cons
- −Date: longer identifiers, harder to remember
- −Integer: fewer versions discourage breaking changes (good or bad)
- −Date: may imply time-based support windows that aren't intended
Version-specific code vs transformer chain
Pros
- +Specific code: clear separation, each version is self-contained
- +Transformer: one codebase, backward compat as layers
- +Transformer: less code duplication, easier bug fixes
Cons
- −Specific code: duplication across versions, bug fixes needed in all
- −Transformer: growing chain of transforms adds complexity
- −Transformer: harder to understand the effective behavior per version
Learn API Versioning
Copy a learning context prompt for an AI assistant. It keeps assumptions visible and asks for tradeoffs, cost drivers, failure modes, and what to measure before adding complexity.
Explore More Capabilities
Audit Logging
Append-only event stores, tamper-proof records, and compliance-ready activity trails
Caching Strategies
Cache-aside, write-through, invalidation patterns, and distributed caching with Redis
CDN & Asset Delivery
Origin shielding, cache hierarchies, purging strategies, and global content distribution