API Versioning Strategies: Real Lessons from Production Incidents and Fixes
Source: Dev.to
Introduction
There’s nothing quite like the sinking feeling when you realize your shiny new API change just broke half of your clients in production. Been there. More than once. If you haven’t yet, you will. Let’s talk honestly about how to handle breaking API changes before your users light up your inbox.
No matter how much you plan, business requirements shift. Data models evolve. That UserDTO you designed in 2018 is now missing fields, has fields you regret, and uses camelCase for reasons no one remembers. Sometimes, you just have to break things.
But there’s a world of difference between breaking things thoughtfully and unleashing chaos. Let’s get into the real options. I’ve wrestled with all of these in C#/.NET (Web API, Clean Architecture), Python (FastAPI), and when integrating with frontend teams using React.
URI Versioning
Example: /api/v1/orders, /api/v2/orders
Pros
- Easy to implement and discover
- Multiple versions can run side‑by‑side
Cons
- Clients might ignore newer versions
- You end up supporting
/v1forever if you’re not careful
What Actually Happened:
/v1 was kept “just for a few months.” Two years later, it was still there, with a frightening amount of traffic. Sunsetting old versions is a project, not a toggle.
Header Versioning
Example:
GET /orders
Api-Version: 2
Pros
- Keeps URLs clean
- Lets you version at a granular level (per resource)
Cons
- Harder for humans to debug
- Some proxies and tools strip custom headers (found this out the hard way)
Production Pain: (details omitted for brevity)
Query Parameter Versioning
Example: /orders?version=2
Pros
- Simple to test and roll out
- Visible to clients
Cons
- Feels a bit hacky (though sometimes that’s fine)
- Can make caching and routing trickier
Lessons Learned
- Support multiple versions for a while. Plan for it; don’t assume everyone upgrades on day one.
- Communicate breaking changes early and loudly. Internal docs, changelogs, and Slack reminders. Assume nobody reads them, then remind them again.
- Automate testing across all supported versions. CI that only checks v2? That’s a future incident waiting to happen.
- Set a realistic sunset policy. Be ready to negotiate with that one critical partner who never migrates.
Code Example: Minimal Versioning in ASP.NET Core
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/orders")]
public class OrdersController : ControllerBase
{
[HttpGet]
public IActionResult Get() { /* ... */ }
}
You can combine this with Microsoft.AspNetCore.Mvc.Versioning for more flexibility. Just remember, adding versioning later is way harder than starting with it.
Contract‑First Approach for Frontend Teams
React devs hate surprises. We broke the contract once by removing a field. The result? Frontend blew up, users saw blank pages, and nobody was happy.
Solution: Use OpenAPI specs as contracts.
- Generate types automatically for React using tools like openapi-typescript.
- Run contract tests in CI to catch breaking changes before deploy.
When to Make a Breaking Change (and When Not To)
- Only break when you must. If you can deprecate a field or add new ones without breaking, do that.
- Batch breaking changes. If you’re going to make a v2, make it count. Don’t version every tiny tweak.
- Give clients a migration path. Feature flags, dual writes, or accept both old and new formats for a while.
What We Can Do Today
- Pick a versioning strategy now, not later. Even if you think you won’t need it, you probably will.
- Automate backward‑compatibility tests. Don’t trust yourself to remember all the edge cases.
- Document and communicate. Docs are great, but Slack reminders and real conversations prevent surprises.
- Sunset old versions ruthlessly. Expect pushback, but enforce it.
If you’ve survived a gnarly API versioning incident (or you strongly disagree with my approach), I genuinely want to hear your story. What’s the worst versioning pain you’ve dealt with? Or, if you’ve found a strategy that actually made your life easier, drop it in the comments.