Fundamentals/API Design

API Versioning

API Design

Visual Representation

Rendering diagram...

What is it?

πŸ“± Think of it like phone updates:

When Apple releases iOS 18, they don't kill iOS 17 immediately. Millions of older iPhones still run iOS 17. Both versions coexist. Over time, older versions are deprecated (no longer supported).

API Versioning is the same β€” when you change your API (add fields, rename endpoints, change response format), you don't break ALL existing apps/clients overnight. You release a NEW version (v2) while keeping the OLD version (v1) running. Old apps keep working; new apps use the better version.

Without versioning: you change the API response format on Monday β†’ every mobile app in production crashes because they expect the OLD format β†’ users give 1-star reviews β†’ disaster.

πŸ’‘ Simple Summary: API versioning lets you evolve your API without breaking existing clients. Old version keeps working while new version adds improvements.

How it works β€” Like you're watching it happen

The problem API versioning solves:

  • V1 launched β€” Your API returns: { "user_name": "prakshay", "created": "2024-01-15" }
  • 500 apps use it β€” Third-party developers build apps using YOUR API format.
  • You want to rename β€” "user_name" should be "username" (consistent with industry standards). Response should include "created_at" instead of "created."
  • Without versioning β€” Change the API β†’ 500 apps break immediately β†’ angry developers β†’ mass API key cancellations.
  • With versioning:
  • - Keep /v1/users returning OLD format (user_name, created)

    - Launch /v2/users returning NEW format (username, created_at)

    - Notify developers: "V1 deprecated, V2 available, V1 shutdown in 6 months"

    - Apps migrate at their own pace. No one breaks. βœ…

    Where to put the version:

  • URL path (most common): /api/v1/users, /api/v2/users
  • Query parameter: /api/users?version=2
  • Custom header: X-API-Version: 2
  • Accept header: Accept: application/vnd.myapi.v2+json
  • ❓ But wait β€” GraphQL claims it doesn't need versioning. How?

    GraphQL is additive β€” you ADD new fields without removing old ones. Old queries that ask for old fields still work. New queries can ask for new fields. Since the client specifies exactly what it wants, adding fields doesn't break anyone. REST can't do this easily because the server decides the response shape. That said, even GraphQL eventually needs to deprecate fields, which is its form of "versioning."

    Why should you care? (Interview perspective)

  • 🎯 "How would you handle API evolution?" comes up when designing large-scale APIs
  • Shows you think about backward compatibility and developer experience
  • Demonstrates awareness of real-world operational concerns (you can't update all clients simultaneously)
  • Understanding versioning strategies shows API maturity
  • Key Things to Remember

  • URL versioning is most popular β€” /v1, /v2 in the path. Simple, visible, easy to route. Used by Twitter, Stripe, Google.
  • Header versioning β€” Cleaner URLs but less discoverable. Client sends Accept: application/vnd.github.v3+json. Used by GitHub.
  • Deprecation policy β€” Announce deprecation β†’ give 6-12 months β†’ send warning headers β†’ shut down. Never surprise-kill a version.
  • Breaking vs Non-breaking changes β€” Adding a new field = non-breaking (old clients ignore it). Removing a field = BREAKING. Renaming = BREAKING. Changing type = BREAKING. Only bump version for breaking changes.
  • Sunset header β€” HTTP header "Sunset: Sat, 01 Jan 2025 00:00:00 GMT" tells clients when this version will die. Standard practice.
  • Two versions max β€” Supporting 5+ versions is maintenance hell. Keep current and previous only. Force migration.
  • Feature flags alternative β€” Instead of whole-version bumps, sometimes you can use feature flags: "This user opted into the new response format."
  • Semantic versioning for APIs β€” v1.2.3 (major.minor.patch). Only major version bumps go in URL. Minor/patch = backward compatible.
  • Documentation per version β€” Each version needs its own docs. Stripe's docs have a version selector: "API Version 2023-08-16."
  • Testing all versions β€” Every supported version needs automated tests. Breaking an old version is just as bad as launching a buggy new one.
  • Real Examples You Use Daily

    πŸ’³ Stripe β€” Uses date-based versioning (2023-08-16, 2024-01-15). Each API key is pinned to a version. New features require opting into a newer version. You upgrade at your own pace. This is the gold standard.

    🐦 Twitter β€” URL-based: /1.1/tweets/search vs /2/tweets/search. V2 is completely redesigned. V1.1 ran for YEARS after V2 launched.

    πŸ—ΊοΈ Google Maps β€” /maps/api/place/v1/... Different versions for different feature sets. Old versions deprecated with clear timelines.

    πŸ“± Mobile apps β€” When Instagram changes their API, they must keep old versions running because millions of phones have OLD app versions that won't update for months. API versioning gives them time.

    Common Mistakes in Interviews

    ❌ Saying "just update the API and tell clients to upgrade" β€” You CANNOT force millions of mobile apps to update simultaneously. Some users never update! Versioning gives a graceful migration path.

    ❌ Versioning every minor change β€” Adding an optional field? NOT a new version. Only version on BREAKING changes. Over-versioning creates maintenance burden.

    ❌ Not having a deprecation plan β€” "We'll keep v1 running forever." No! That's unbounded maintenance cost. Set clear sunset dates: "V1 supported until December 2025."

    ❌ Forgetting about mobile apps β€” Web apps can update instantly (deploy new frontend). Mobile apps are installed on user devices β€” old versions persist for months/years. This is the PRIMARY reason API versioning exists.

    ❌ Making backward-incompatible changes without realizing β€” Changing a field from string to integer, removing a field "nobody uses" (someone always uses it), or changing error format β€” all breaking changes that need a new version.

    🎯 Interview One-Liner

    "I'd version the API in the URL path (/v1, /v2) for discoverability, only bump versions for breaking changes, maintain a clear deprecation policy with Sunset headers, and ensure backward compatibility by adding new fields as optional rather than modifying or removing existing ones."

    Interview Q&A

    Q: URL versioning vs header versioning β€” trade-offs?

    URL (/v1/users): visible, easy to debug (you see the version in browser/logs), simple routing (nginx routes /v1/* to old service, /v2/* to new). But "pollutes" the URL (purists say version isn't a resource). Header (Accept: application/vnd.api.v2+json): clean URLs, but hidden (harder to debug, share, and cache since URLs look identical). Industry standard: URL versioning wins for public APIs due to simplicity.

    Q: What constitutes a "breaking change" in an API?

    Changes that break existing clients: (1) Removing a field from response, (2) Renaming a field, (3) Changing a field's type (string β†’ integer), (4) Changing the URL pattern, (5) Making a previously optional field required, (6) Changing error response format, (7) Changing authentication mechanism. NON-breaking: adding new optional fields, adding new endpoints, adding new optional query parameters.

    Q: How does Stripe handle API versioning?

    Date-based versions (2023-08-16). Each account is pinned to the version it was created with. Stripe transforms responses internally β€” new code, but response is transformed to match the customer's pinned version. Customer can upgrade by changing their pinned version. This is elegant: one codebase, many version "views." New features require newer versions.

    Q: How would you sunset an old API version?

    Timeline: (1) Announce deprecation 6-12 months before shutdown. (2) Add Sunset header to all V1 responses. (3) Send emails to V1 API key users. (4) Add deprecation warnings to V1 responses. (5) Track V1 usage β€” contact remaining heavy users directly. (6) Return 410 Gone + migration docs after sunset date. (7) Never surprise-kill β€” give at least 3 warnings + final deadline extension if usage is still high.

    Q: You're building a mobile-first app. How does versioning help?

    Mobile apps installed on phones can't be force-updated. If v1.0 of your app expects API format X, but you change the API to format Y β€” every user who hasn't updated their app sees errors. With versioning: app v1.0 calls /api/v1. App v2.0 calls /api/v2. Both work simultaneously. You can sunset /api/v1 only after 95%+ of users have updated (track via version analytics).

    Quick Quiz

    1/5

    You rename a field from "user_name" to "username" in your API response. 1000 existing apps break. What should you have done?