Backward-Compatible API Versioning in Production

How to evolve REST and gRPC APIs without breaking mobile clients and partner integrations.

2 min readAPI DesignNode.jsArchitectureBest Practices

You cannot force every mobile user to update the app on the day you deploy. Backward-compatible API evolution is how backend teams ship weekly without breaking clients in the wild.

Rules that work

  1. Additive changes are safe — New optional fields, new endpoints, new enum values (if clients ignore unknowns).
  2. Breaking changes need a new version/v2/... or new package in gRPC.
  3. Never repurpose fields — Changing status: 1 from “pending” to “cancelled” breaks everyone.
  4. Deprecate in phases — Mark old fields deprecated in docs, log usage, remove only when traffic is zero.

Versioning styles

| Style | Example | Notes | |-------|---------|--------| | URL path | /api/v1/orders | Clear, easy to route at gateway | | Header | Accept-Version: 2 | Keeps URLs clean | | gRPC package | order.v2.OrderService | Strong contracts with protobuf |

Pick one per product and stick to it.

Expand and contract (database)

  1. Expand: Add new column nullable or with default.
  2. Deploy code that writes both old and new shapes.
  3. Migrate data if needed.
  4. Contract: Stop writing old fields; remove later.

Same idea applies to event schemas in Kafka.

Testing compatibility

  • Contract tests against golden JSON fixtures from oldest supported app version.
  • Canary deploy with shadow traffic comparison.

Real-world impact

On payment and super-app backends, backward compatibility plus zero-downtime deploys meant we could run multiple app versions against one API surface for months.

Takeaway

Treat your public API as a long-lived contract. Version when you must; evolve additively when you can. Your future self (and mobile team) will thank you.