Why APIs Need Versioning

APIs evolve over time. New features are added, data models change, and old patterns are replaced. Without versioning, any change could break existing clients. API versioning allows backward-incompatible changes to coexist with older versions, giving clients time to migrate.

Breaking vs. Non-Breaking Changes

Non-breaking changes (safe to deploy without a new version):

  • Adding new optional fields to responses
  • Adding new endpoints
  • Adding optional query parameters
  • Relaxing validation rules
  • Fixing bugs in error messages

Breaking changes (require a new version):

  • Removing or renaming a field
  • Changing a field’s data type
  • Making an optional field required
  • Changing the URL structure
  • Modifying authentication mechanisms
  • Changing error response format

Versioning Strategies

URL Path Versioning

The most common approach — the version is part of the URL:

GET /api/v1/users
GET /api/v2/users

Pros: Easy to see, easy to cache, easy to route Cons: URL changes between versions, breaks REST purist principles

Header Versioning

Version specified in a custom header or Accept header:

GET /api/users
Accept: application/vnd.myapi.v2+json

Or custom header:

GET /api/users
X-API-Version: 2

Pros: Clean URLs, same resource different representations Cons: Hidden from URL, harder to debug and test in browser

Query Parameter Versioning

Version as a query parameter:

GET /api/users?version=2

Pros: Easy to switch, visible in URL Cons: Can be confused with resource filtering, caching complications

Comparison

AspectURL PathHeaderQuery Param
VisibilityHighLowMedium
CachingEasyComplexComplex
API gateway routingEasyModerateModerate
Browser testingEasyHardEasy
REST complianceLowHighMedium
Industry adoptionMost commonMediumLess common

Testing API Versioning

Backward Compatibility Tests

For each new version, verify that existing clients still work:

  1. Response structure: v1 clients should still receive v1 response format
  2. Field presence: No fields removed from v1 responses
  3. Data types: No type changes in v1 responses
  4. Error formats: v1 error responses unchanged
  5. Authentication: v1 auth mechanisms still work

Version-Specific Tests

Testv1v2
GET /users response{name: "Alice"}{firstName: "Alice", lastName: "..."}
Pagination?page=1&per_page=20?cursor=abc&limit=20
Error format{error: "Not found"}{error: {code: "NOT_FOUND", message: "..."}}

Deprecation Testing

When a version is being deprecated, verify:

  1. Deprecation headers present:
Deprecation: true
Sunset: Sat, 01 Mar 2026 00:00:00 GMT
Link: </api/v2/docs>; rel="successor-version"
  1. Warning headers: Some APIs include Warning headers with deprecation info
  2. Documentation links: Response or headers link to migration guide
  3. Sunset timeline: The deprecated version continues working until the sunset date
  4. After sunset: Returns 410 Gone or redirects to new version

Default Version Behavior

Test what happens when no version is specified:

  • Does the API default to the latest version?
  • Does the API default to v1 (safest for backward compatibility)?
  • Does the API return an error requiring explicit version?

Cross-Version Data Consistency

Data created in v1 should be accessible in v2 and vice versa (unless intentionally separated):

1. POST /api/v1/users → Create user (v1 format)
2. GET /api/v2/users/{id} → Should return user in v2 format
3. PUT /api/v2/users/{id} → Update in v2 format
4. GET /api/v1/users/{id} → Should still return v1 format

Migration Testing

When clients migrate from v1 to v2:

  • Verify all v1 functionality has v2 equivalents
  • Test that v1 data is properly transformed in v2 responses
  • Check that v1-specific features that are removed have documented alternatives
  • Ensure authentication tokens work across both versions

Common Versioning Bugs

BugHow to Detect
v1 response includes v2-only fieldsCompare v1 response schema to docs
v2 breaks v1 contractRun v1 test suite against current deployment
No deprecation noticeCheck response headers for deprecated versions
Data inconsistency between versionsCreate in v1, read in v2, compare
Default version changes silentlyTest requests without explicit version
Authentication mismatchUse v1 tokens on v2 endpoints

Hands-On Exercise

  1. Compare versions: If using an API with multiple versions (GitHub API v3 vs GraphQL v4), compare the same operation across versions.
  2. Test backward compatibility: Make requests to both old and new versions of an API endpoint and verify data consistency.
  3. Create a migration checklist: For a hypothetical v1 to v2 migration, list all changes and their test scenarios.
  4. Check deprecation: Find a deprecated API endpoint and verify it includes proper deprecation headers and sunset dates.

Key Takeaways

  • API versioning enables backward-incompatible changes while keeping existing clients functional
  • URL path versioning (/api/v2/) is the most common approach; header versioning is the most REST-compliant
  • Breaking changes (removing fields, changing types) require new versions; non-breaking changes (adding optional fields) do not
  • Test backward compatibility by running old version test suites against new deployments
  • Verify deprecation communication: headers, sunset dates, documentation links, and migration guides