Bird
Raised Fist0

API Versioning Strategies - URL, Header, Content Negotiation

HLDmediumInterview freq: common
Choose your preparation mode4 modes available
🏗️
API Versioning Strategies - URL, Header, Content Negotiation
Designing scalable and maintainable API versioning using URL, Header, and Content Negotiation strategies
mediumAPI Design and GatewayFreq: common
⚙️
Functional Requirements
  • Support multiple API versions concurrently to enable backward compatibility
  • Allow clients to specify API version via URL path, HTTP headers, or content negotiation
  • Route requests to appropriate service implementations based on version
  • Enable smooth client migration from older to newer API versions without downtime
  • Provide clear error handling for unsupported or deprecated versions
  • Allow deprecation and eventual removal of old API versions
📊
Non-Functional Requirements
  • Handle 2 billion daily active users (DAU) with peak QPS of 925K write requests/sec
  • Ensure 99.99% availability for API gateway and version routing components
  • Maintain API response latency under 100ms P99 including version resolution overhead
  • Support horizontal scaling of version routing logic to handle peak load
  • Ensure backward compatibility without breaking existing clients during deployments
❓
Clarifying Questions to Ask
  • What is the expected number of concurrent API versions to support?
  • Are there any constraints on client capabilities to send headers or modify URLs?
  • Is versioning required for all API endpoints or only a subset?
  • What is the expected client migration timeline from old to new versions?
  • Should the system support automatic fallback if a version is not specified?
  • Are there any existing API gateways or proxies in use that influence versioning?
Out of Scope
✗ Design of the underlying business logic or microservices behind APIs
✗ Authentication and authorization mechanisms for API access
✗ Detailed client SDK implementation for version negotiation
📌
Assumptions
  • 2 billion DAU
  • 40 API calls per user per day on average
  • Average request payload size 100 bytes
  • Retention period for logs and metrics: 30 days
⚡
QPS Calculation
write qps2,000,000,000 × 40 / 86400 ≈ 925,926 requests/sec
peak write qpsAssuming 3x peak factor: 925,926 × 3 ≈ 2,777,778 requests/sec
read qpsAssuming read-heavy ratio 5:1, read_qps = 925,926 × 5 ≈ 4,629,630 requests/sec
💾
Storage Estimate
daily storage bytes925,926 requests/sec × 100 bytes × 86400 sec ≈ 8 TB/day
retention storage bytes8 TB/day × 30 days = 240 TB
with replication240 TB × 3 (replication factor) = 720 TB total storage
🖥️
Server Estimate
api gateway serversAssuming 10K RPS per server, 2,777,778 peak write QPS / 10,000 ≈ 278 servers
version router serversAssuming 5K QPS per server for routing logic, 2,777,778 / 5,000 ≈ 556 servers
🏗️
Architecture Diagram
Architecture Diagram
          +----------------------+
          |      Clients         |
          +----------+-----------+
                     |
        +------------v-------------+
        |      API Gateway         |
        | (Nginx + Envoy Proxy)   |
        +------------+-------------+
                     |
    +----------------v----------------+
    |   Version Routing Service       |
    | (Envoy Filter + Custom Router) |
    +----------------+----------------+
                     |
    +----------------v----------------+
    |   Microservices (v1, v2, v3...) |
    +---------------------------------+
                     |
          +----------v-----------+
          |   Monitoring & Logs  |
          +---------------------+
🧩
Components
API Gateway (Nginx + Envoy Proxy)
Entry point for all client requests; performs TLS termination, rate limiting, and initial routing
Why thisEnvoy supports dynamic routing and header manipulation needed for versioning; Nginx handles TLS efficiently
Why notAWS API Gateway lacks fine-grained control and high customization needed for version negotiation at 3M QPS
Version Routing Service (Envoy Filter + Custom Router)
Determines API version from URL, headers, or content negotiation and routes requests to correct microservice version
Why thisEnvoy’s extensible filter chain allows custom logic for version extraction and routing with low latency
Why notUsing a monolithic router service would add latency and reduce scalability at 2.7M QPS
Microservices per API Version
Implement business logic for each API version independently to isolate changes and enable backward compatibility
Why thisMicroservices allow independent deployment and scaling per version, reducing risk of breaking clients
Why notSingle codebase with feature flags complicates testing and increases risk of regressions at scale
Monitoring & Logging System (Elastic Stack + Prometheus)
Collects metrics and logs for version usage, error rates, and latency to enable observability and alerting
Why thisElastic Stack supports high ingestion rates and flexible querying; Prometheus provides real-time metrics
Why notSimple log files or basic metrics lack scalability and real-time alerting needed for 99.99% SLA
Configuration Management (Consul + GitOps)
Manages version routing rules and deprecation schedules with auditability and rollback support
Why thisConsul provides dynamic config updates with low latency; GitOps ensures version control and traceability
Why notManual config updates or static files cause downtime and risk inconsistent routing at scale
🔄
Data Flow
1
Client → API Gateway
Client sends API request with version info in URL, header, or Accept header
Supports all three versioning strategies for flexibility
2
API Gateway → Version Routing Service
Gateway forwards request with metadata to routing service for version resolution
Offloads version parsing logic from gateway to dedicated service
3
Version Routing Service → Microservice (correct version)
Routes request to appropriate microservice version based on extracted version
Ensures backward compatibility and isolated deployments
4
Microservice → Version Routing Service
Microservice processes request and returns response
Response includes version info for client awareness
5
Version Routing Service → API Gateway
Routes response back to gateway
Gateway forwards response to client
6
API Gateway → Client
Client receives response with version info
Client can handle version-specific response accordingly
🗄️
api_versions
PK: api_name
api_name (string)version (string)status (string)release_date (timestamp)deprecation_date (timestamp)
Stores metadata about each API version
🗄️
version_routing_rules
PK: api_name
api_name (string)rule_id (uuid)match_type (string)match_value (string)target_version (string)priority (int)
Rules for routing requests based on version info
🔌
GET
Request
{
  "path_params": [
    "version"
  ],
  "headers": [
    "Accept-Version"
  ],
  "query_params": []
}
Response
{
  "body": "Resource data for specified version",
  "headers": [
    "Content-Version"
  ]
}
URL versioning with fallback to Accept-Version header
🔌
GET
Request
{
  "headers": [
    "API-Version",
    "Accept"
  ],
  "query_params": []
}
Response
{
  "body": "Resource data for specified version",
  "headers": [
    "Content-Version"
  ]
}
Header-based versioning and content negotiation support
🔌
POST
Request
{
  "headers": [
    "API-Version"
  ],
  "body": "Payload for resource creation"
}
Response
{
  "body": "Creation confirmation with version info",
  "headers": [
    "Content-Version"
  ]
}
Supports versioning in write operations via headers

Practice

(1/5)
1. During a flash sale, the system must handle sudden bursts of 10x traffic within 60 seconds, ensuring order preservation of purchase requests per user session. Which component best handles this requirement?
easy
A. Use a relational database with row-level locking to serialize requests
B. Use Redis as a cache to store purchase requests temporarily
C. Use Kafka with partitioning keyed by user session ID to preserve order
D. Use a simple HTTP load balancer to distribute requests evenly

Solution

  1. Step 1: Identify ordering requirement per user session

    Kafka partitions keyed by user session ID guarantee ordering within that partition, ensuring requests are processed sequentially per user.
  2. Step 2: Evaluate alternatives

    Relational DB row-level locking (A) causes high contention and poor scalability under 10x burst. Redis (C) lacks strong ordering guarantees and durability. Load balancer (D) only distributes load but does not preserve order.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Kafka partition key -> per-session ordering guaranteed [check]
Hint: Kafka partitions preserve order per key [check]
Common Mistakes:
2. A high-throughput web service must handle millions of requests per second with minimal latency and no session affinity. Which architectural component best supports this requirement?
easy
A. Stateful application servers maintaining user sessions in memory
B. Stateless application servers behind a load balancer with external session storage
C. Sticky sessions on load balancer directing requests to the same server
D. Monolithic server with local session cache for each user

Solution

  1. Step 1: Identify stateless server benefits

    Stateless servers allow horizontal scaling without session affinity, reducing latency and complexity.
  2. Step 2: Evaluate session management

    External session storage decouples session state from servers, enabling load balancer to distribute requests evenly.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Stateless servers + external session store -> scalable, low-latency [check]
Hint: Stateless servers + external session store scale best [check]
Common Mistakes:
3. In a relational database transaction flow, at Step 4 the transaction manager checks the lock table before committing. What happens if the lock is held by another transaction at this step?
easy
A. The transaction manager immediately aborts the transaction without retry
B. The transaction manager waits (blocks) until the lock is released or a timeout occurs
C. The transaction manager skips the lock check and commits anyway
D. The transaction manager escalates the lock to a global lock manager

Solution

  1. Step 1: Understand lock table role at Step 4

    The transaction manager uses the lock table to ensure no conflicting writes before commit.
  2. Step 2: Behavior on lock contention

    If the lock is held, the transaction manager blocks to maintain serializability, waiting or timing out rather than aborting immediately or skipping checks.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Lock contention causes blocking until release or timeout [check]
Hint: Lock held -> transaction blocks, no skipping or immediate abort [check]
Common Mistakes:
4. If the Kafka cluster used for fan-out events crashes and loses the latest messages, what exactly breaks in the newsfeed delivery?
medium
A. The system falls back to synchronous push, increasing write latency
B. Posts published just before the crash are lost; followers never see them
C. Followers receive duplicate posts due to re-delivery attempts
D. Delivery Workers block indefinitely waiting for missing messages

Solution

  1. Step 1: Understand Kafka's role in fan-out

    Kafka buffers fan-out events; Delivery Workers consume from Kafka to deliver posts asynchronously.
  2. Step 2: Trace failure impact

    If Kafka crashes and loses messages, Delivery Workers waiting on those messages block or stall, causing delivery delays.
  3. Step 3: Eliminate other options

    Duplicates (A) occur with retries, not loss. Lost posts (B) happen but are not the immediate failure mode. Fallback to synchronous push (D) is not automatic.
  4. Final Answer:

    Option D -> Option D
  5. Quick Check:

    Kafka crash causes Delivery Workers to block waiting for lost messages [check]
Hint: Kafka crash blocks Delivery Workers waiting for messages [check]
Common Mistakes:
5. At 10x scale, which component in the recommendation system is most likely to fail first due to stateful connection limits, and why?
hard
A. The Kafka cluster, because partition count limits throughput and causes producer backpressure
B. The recommendation API servers, because maintaining user session state for personalized recommendations exhausts memory
C. The embedding storage database, because it cannot shard embeddings by user ID efficiently
D. The embedding service, because it holds in-memory caches that grow unbounded with user scale

Solution

  1. Step 1: Identify bottleneck at 10x scale

    Kafka partitions limit throughput; increasing partitions is complex and causes rebalancing overhead.
  2. Step 2: Evaluate other components

    Embedding DB (A) can shard by user ID; API servers (C) are typically stateless or use external session stores; embedding service caches (D) can be evicted or sharded. Kafka's partition limit causes producer backpressure and stalls event flow first.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Kafka partition limit -> throughput bottleneck -> backpressure [check]
Hint: Kafka partitions limit throughput first at scale [check]
Common Mistakes:
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard