Introduction
In our last lesson, we explored the fundamental differences between synchronous RESTful architectures and asynchronous event-driven systems. We established that in both patterns, the Application Programming Interface (API) acts as a critical contract between services.
Today, we address a crucial real-world problem: what happens when that contract needs to change? Your systems will inevitably evolve with new features, bug fixes, or data model updates. How do you manage these changes without breaking existing client integrations and destroying trust?
This lesson focuses on precisely that. Your learning outcome is to evaluate API versioning strategies (path, header, and query parameter) and justify a choice based on client impact and maintainability. This is a very common topic in backend interviews that tests your ability to think about long-term design, trade-offs, and the developer experience of your API's consumers.
The API as a Contract and the Need for Versioning
An API is a promise. Clients build their applications relying on the specific structure of your endpoints, request payloads, and response objects. Any uncommunicated change can lead to production failures for them.
When a change is required, you face a choice:
- Additive Change (Backward-Compatible): You add new, optional fields or new endpoints. Existing clients are unaffected. This is the ideal scenario but isn't always possible.
- Breaking Change (Backward-Incompatible): You rename a field, change a data type, remove an endpoint, or make a previously optional field required. These changes will break existing clients if they are not prepared.
Explicit versioning is the strategy you use to manage breaking changes gracefully. It allows you to release new versions of your API while maintaining older, stable versions for existing clients, giving them time to migrate.

Core Versioning Strategies: A Trade-Off Analysis
There are three primary methods for clients to specify which version of an API they want to use. Each has significant implications for clients and for your own development and maintenance.
To get a clear overview of these strategies, let's start with a video.
Web API - Explicit Versioning | URI vs Headers vs Request Params | SemVer | Sunset Headers
The video 'Web API - Explicit Versioning' by Ambient Coder provides a concise introduction to the three main explicit versioning techniques.
Please watch the following segments, paying close attention to the pros and cons mentioned for each approach: (01:51 - 03:22): Versioning in the URI path. (03:22 - 04:09): Versioning with HTTP headers. (04:09 - 04:49): Versioning with request parameters (query parameters).
Now, let's consolidate this information and structure it for an interview discussion.
1. Path Versioning
This is the most common and straightforward approach. The version is embedded directly into the URL path.
Example: https://api.example.com/v1/orders

- Pros:
- Highly Visible & Simple: It's immediately obvious to anyone looking at the URL which version is being used. It's easy to test in a browser.
- Cacheable: Since the URL is unique for each version, responses can be easily cached by CDNs and browsers without special configuration.
- Cons:
- Pollutes the URI: Critics, including Roy Fielding (the creator of REST), argue that a URI should identify a resource, which is timeless. The representation of that resource might change, but the resource itself does not. Path versioning technically creates multiple URIs for the same resource (e.g.,
/v1/orders/123and/v2/orders/123both point to the same order). - Can Lead to Code Duplication: If not handled carefully, this can lead to sprawling
if/elseblocks in your routing or even separate codebases for each version, increasing maintenance overhead.
- Pollutes the URI: Critics, including Roy Fielding (the creator of REST), argue that a URI should identify a resource, which is timeless. The representation of that resource might change, but the resource itself does not. Path versioning technically creates multiple URIs for the same resource (e.g.,
2. Header Versioning
With this approach, the URL remains clean and stable, while the version is specified in an HTTP header. This can be done in two ways:
- Custom Header:
X-API-Version: 2 - Accept Header:
Accept: application/vnd.example.v2+json(This is known as "media type versioning" or "content negotiation" and is considered more purely RESTful).
- Pros:
- Clean URLs: The URI represents the resource, not its version, which aligns perfectly with REST principles.
https://api.example.com/orders/123is the timeless identifier for that order. - Maintains URL Stability: You never have to change the resource's URL, which is good for permalinks and long-term stability.
- Clean URLs: The URI represents the resource, not its version, which aligns perfectly with REST principles.
- Cons:
- Less Visible: It's not obvious from the URL which version is being used, making browser-based exploration and debugging harder.
- More Complex for Clients: Requires clients to correctly configure their HTTP headers for every request.
- Caching Complexity: Caching layers (like CDNs or proxies) need to be configured to include the versioning header in their cache key (e.g., using the
Vary: Acceptresponse header).
3. Query Parameter Versioning
This strategy includes the version as a query parameter in the URL.
Example: https://api.example.com/orders?version=1
- Pros:
- Clean URL Path: Like header versioning, it keeps the base URL path stable.
- Easy to Test: Like path versioning, it's easy to switch versions directly in a browser's address bar.
- Can Have a Default: It's easy to fall back to a default version if the parameter is omitted.
- Cons:
- Caching Issues: Similar to header versioning, many caching proxies do not include query parameters in the cache key by default, which can lead to clients receiving the wrong version from a cache.
- Messy URLs: While the path is clean, a proliferation of query parameters can make the full URL unwieldy.
- Routing Complexity: Can complicate routing logic within your application, as the path alone is not sufficient to determine the controller.
Structuring Your Interview Answer: The Justification Framework
A senior-level answer doesn't just list the options; it justifies a choice based on context. Here is a framework to structure your thoughts, combining what we've learned with practical implementation details.
Since you have a strong background in Django REST Framework (DRF), it's useful to know that DRF supports all these strategies out of the box. Showing you can connect architectural concepts to your framework of choice is a huge plus.
How to Implement API Versioning in Django REST Framework
The blog post 'How to Implement API Versioning in Django REST Framework' from OneUptime provides a fantastic summary table and practical code examples. We'll use it to anchor our discussion.
You don't need to implement the code now, but focus on these parts to build your justification framework: 'Versioning Strategies Comparison' Table: This is the most important part. Study this table to internalize the pros, cons, and best use cases for each strategy. Configuration Examples: Briefly look at the REST_FRAMEWORK settings for URLPathVersioning, QueryParameterVersioning, and AcceptHeaderVersioning. Notice how a simple configuration change in settings.py can switch the entire strategy. This demonstrates your practical knowledge.
Here is a summary table inspired by that resource, framed for an interview response:
| Strategy | When to Justify Its Use | Key Trade-Off |
|---|---|---|
| URL Path | For public-facing APIs where ease-of-use, discoverability, and simple browser testing are critical. It's the most common and least surprising for many developers. | You are prioritizing client simplicity and cacheability over REST purity. You accept that the URL is tied to a specific representation. |
| Header | For internal microservices or sophisticated external clients (e.g., mobile apps) where long-term URL stability is paramount. It’s the cleanest, most "correct" RESTful approach. | You are prioritizing architectural purity and stable resource identifiers over the simplicity of browser-based testing. You accept more client and caching configuration complexity. |
| Query Parameter | As a pragmatic middle-ground, often for internal APIs where you want stable paths but need the ease of browser testing. It is a compromise. | You are prioritizing URL path stability but are willing to accept potential caching issues and less clean full URLs. |
An Advanced Perspective: Stripe's Date-Based Versioning
To demonstrate senior-level thinking, it's powerful to bring in a real-world example from a company known for its API excellence. Stripe uses a sophisticated form of header-based versioning that prioritizes developer trust above all else.
Why Stripe’s API Never Breaks | Date-Based Versioning Explained
The video 'Why Stripe’s API Never Breaks' by ByteMonk is an outstanding deep dive into a production-grade versioning system. This is an excellent case study to discuss in an interview.
Watch these segments and focus on the philosophy behind Stripe's decisions: (00:56 - 02:31): Understand the problem Stripe is solving: forced migrations break developer trust. (02:31 - 04:45): Pay attention to the core mechanism: pinning accounts to a date-stamped version and using headers to override it. (04:45 - 07:07): Grasp the internal architecture: the request and response compatibility layers. This is a brilliant concept that allows their internal logic to always work on the latest version, while transforming data back for older clients.
Key talking points from the Stripe model:
- Philosophy: Never break a client's integration. Stability builds trust, which is a competitive advantage.
- Mechanism: Accounts are "pinned" to the API version (a date, like
2022-11-15) they were created with. All requests from that account default to that version's behavior. - Flexibility: A developer can upgrade their account's pinned version in the dashboard at their own pace, or override the version for a single request using the
Stripe-Versionheader to test new features safely. - Internal Architecture: Stripe's core logic is always written against the newest API version. A response compatibility layer sits at the edge, transforming the "modern" response back into the format the client's old version expects. This avoids polluting the core business logic with legacy concerns.
Mentioning this in an interview shows you think beyond the basic patterns and consider the long-term strategic value of API design.
Deprecation: Completing the Lifecycle
Versioning isn't just about creating new versions; it's also about retiring old ones. Maintaining many versions is costly. A complete versioning strategy must include a deprecation plan.
- Communicate Clearly: Use documentation, newsletters, and blog posts to announce deprecation schedules far in advance.
- Use Deprecation Headers:
- The
Sunsetheader (RFC 8594) signals the date/time when an endpoint will be shut down.
Sunset: Tue, 31 Dec 2024 23:59:59 GMT - A custom
X-Deprecated: trueheader can also be used.
- The
- Log Usage: Monitor which clients are still using old versions so you can contact them directly.
This shows the interviewer you have a production-ready mindset.
Conclusion
You are now equipped to evaluate API versioning strategies like a senior engineer.
Key Takeaways:
- The "Why": Versioning is necessary to introduce breaking changes without disrupting existing clients.
- The "How": The main strategies are Path, Header, and Query Parameter. Each has clear trade-offs.
- The "Justification": The best choice depends on the context. Justify your decision based on client sophistication, the need for URL stability, and cacheability. There is no single "right" answer.
- The "Advanced" View: Citing Stripe's date-based versioning demonstrates a deep understanding of API design philosophy and long-term maintainability.
- The "Lifecycle": A complete strategy includes a clear plan for deprecating and retiring old versions.
Preview of the Next Lesson:
Now that we know how to manage different versions of our API, we need to ensure that the interactions with it are reliable, especially in a distributed world where network failures can cause clients to retry requests. In our next lesson, we will explore idempotency, a critical property for building robust and predictable APIs that can safely handle repeated requests.