Why API Versioning Is Non-Negotiable
The moment your API has an external consumer — whether that's a mobile app, a third-party integration, or another team's service — you have a contract. Breaking that contract by changing response shapes, removing fields, or altering behavior causes real downstream failures.
Versioning gives you the ability to evolve your API over time without forcing every consumer to update immediately. Done well, it's invisible to end users and painless for developers.
What Counts as a Breaking Change?
Understanding what breaks clients is the foundation of good versioning strategy. Breaking changes include:
- Removing a field from a response
- Renaming a field (e.g.,
user_name→username) - Changing a field's data type (string to integer)
- Removing an endpoint entirely
- Changing authentication requirements
- Altering error response formats
Non-breaking changes (usually safe without a new version) include adding new optional fields, adding new endpoints, or expanding enum values.
Strategy 1: URL Path Versioning
The most widely used approach. The version number is embedded in the URL:
GET /v1/users
GET /v2/users
Pros
- Immediately obvious and discoverable
- Easy to test in a browser or with curl
- Simple to route at the infrastructure level
Cons
- URLs are technically supposed to represent resources, not versions
- Can lead to code duplication if not carefully managed
Verdict: Best default choice for most APIs. Used by Stripe, GitHub, and Twilio.
Strategy 2: Request Header Versioning
The version is specified in a custom HTTP header:
GET /users HTTP/1.1
API-Version: 2024-01-01
Pros
- Keeps URLs clean and resource-focused
- Date-based versions (used by Stripe) are intuitive
Cons
- Less discoverable — developers must read docs to know the header name
- Harder to test quickly in a browser
Verdict: Elegant and RESTfully correct, but requires better documentation.
Strategy 3: Query Parameter Versioning
GET /users?version=2
This approach is simple but generally discouraged for production APIs. It's hard to enforce at the routing layer and can be accidentally omitted.
Comparison Table
| Strategy | Discoverability | Clean URLs | Routing Simplicity | Industry Usage |
|---|---|---|---|---|
URL Path (/v1/) | High | No | High | Very common |
| Header | Low | Yes | Medium | Common (enterprise) |
| Query Param | High | No | Low | Less common |
Managing Multiple Versions in Practice
- Maintain a changelog — document every change, breaking or otherwise, with dates and migration guides.
- Set a deprecation policy — give consumers a minimum of 6–12 months notice before sunsetting a version.
- Use deprecation headers — return a
Deprecationheader on old version responses to alert developers automatically. - Limit the number of supported versions — supporting more than 2–3 active versions simultaneously becomes a maintenance burden.
- Design for extensibility from day one — use objects instead of flat fields so you can add properties without breaking existing consumers.
Golden Rule
The best versioning strategy is one that your team will actually maintain consistently. Don't over-engineer — pick a strategy, document it clearly, and stick to it across your entire API surface.