04
Product
16
Backend
09
Auth
12
iOS
07
Infra
02
Real-Time

Use GraphQL schema evolution for API compatibility

ADR-0008 ACCEPTED · 2025-07-11
Use GraphQL Schema Evolution for API Compatibility Across Client Versions

Context

Unlike web applications where frontend and backend deploy together and every client immediately gets the new bundle, native iOS apps update on the device's schedule. Users delay updates for weeks. The App Store review process adds days of delay even when we need to ship a fix. To keep shipping early and often on the backend, we need the API to stay compatible with older app versions.

We tried contract testing first — got as far as a Pact broker running in Nomad and a Rust provider test — but the Swift Pact libraries didn't work well with modern Xcode. The iOS side was the blocker, and maintaining broker infrastructure for one developer wasn't worth it when the real need was simpler: don't break old clients.

Decision

Use GraphQL Schema Evolution best practices to maintain backward compatibility across client versions.

Core principles:

  1. Additive-only changes - Never remove fields, only add new ones
  2. Field deprecation - Mark old fields as @deprecated instead of removal
  3. Staged migration - Add new fields alongside old ones, migrate gradually
  4. Schema diff checking - Use automated tools to prevent breaking changes in CI

Implementation:

  • All schema changes must be backward compatible
  • Use @deprecated directive for fields being phased out
  • CI pipeline includes schema diff checking against production
  • Documentation tracks deprecated fields and migration timelines

Consequences

What Becomes Easier

  • Multiple client versions supported - old iOS apps continue working after backend updates
  • Faster deployment cycles - no need to coordinate backend/client releases
  • Solo development friendly - leverages GraphQL's built-in compatibility features
  • Automated safety - CI prevents accidental breaking changes

What Becomes More Difficult

  • Schema grows over time - deprecated fields accumulate until cleaned up
  • Migration coordination - need to track when deprecated fields can be safely removed
  • Additional CI complexity - schema diff checking adds build step