How to Design API Versioning Without Breaking Clients
API versioning is less about /v1 vs /v2 and more about contracts. Here’s a practical approach that keeps old clients working while you ship new features.
Shipping an API is easy. Keeping it usable for years is the hard part. Versioning is how you change shape without stranding the apps already calling you.
Start with a contract, not a number
Before you add /v2, write down what clients can rely on: required fields, error shapes, auth headers, and pagination. If that contract is clear, most “breaking” changes become deliberate choices instead of accidents.
Prefer additive changes
Add optional fields. Keep old fields. Introduce new endpoints for new workflows. Deprecate loudly, remove slowly. Most teams don’t need a major version for every improvement—they need discipline.
- Add, don’t rename
- Never reuse a field for a new meaning
- Document deprecations with a removal date
When you do need a new version
Use a new version when the response shape or auth model changes in a way old clients cannot ignore. Put the version in the URL (/api/v2/...) or in a header—pick one and stick with it. URL versioning is usually easier for debugging and caching.
A simple rollout plan
- Ship v2 behind feature flags or a separate path
- Migrate your own clients first
- Give external clients a sunset window
- Monitor 4xx rates and usage by version
- Only then turn off v1
What to avoid
Don’t silently change types. Don’t remove fields without a deprecation period. Don’t invent a new error format per endpoint. Consistency matters more than cleverness.
Bottom line: version when the contract breaks. Everything else can ship as additive, documented change.