This chapter follows Managing GraphQL schemas in organizations.
Backend teams often learn these pitfalls in production. Each section follows symptom → cause → fix.
Nullability changes break clients differently
Symptom: Web clients crash on null where they expected strings; mobile apps on old schema still send old queries.
Cause: Adding ! to an output field turns previous null responses into GraphQL errors that bubble. Making an input field required breaks operations that omitted it.
Fix: Add new fields instead of tightening nullability; use deprecation. For inputs, add a new optional field or a new input type version.
Required arguments and input fields
Symptom: Deploy succeeds; clients receive validation errors Argument "X" of required type.
Cause: New required argument on a field or new required input property.
Fix: Default the argument in schema or keep it optional until telemetry shows all operations updated.
New enum values and exhaustive switches
Symptom: TypeScript switch throws on unknown enum; UI shows blank state.
Cause: Client generated exhaustive enums; server added a value.
Fix: Treat enums as open sets in UI (default branch); regenerate types. Document enum additions in changelog.
Stale client cache metadata
Symptom: Union fragments stop rendering after deploy though queries succeed.
Cause: Apollo possibleTypes or Graphcache schema JSON missing new implementors.
Fix: Ship client updates with refreshed introspection artifacts in the same release as schema changes.
Persisted query hash mismatches
Symptom: Gateway returns PersistedQueryNotFound or rejects operations.
Cause: Field removed from schema but mobile app still sends old persisted hash.
Fix: Version persisted query sets; support dual hashes during migration; avoid removing fields until usage clears.
Long tail of mobile versions
Symptom: Crash reports reference removed fields weeks after backend cleanup.
Cause: App store review delays; users on old binaries.
Fix: Longer deprecation windows for mobile; feature flags on client to stop selecting fields before backend removal.
Semantic changes without schema diffs
Symptom: Finance reports wrong totals; schema validation passes.
Cause: Resolver now computes total with tax included—same field name, different meaning.
Fix: Add totalWithTax or bump documented API behavior; treat as breaking in comms even if SDL unchanged.
Changing ID format
Symptom: Normalized caches show duplicates; links break.
Cause: Switched from numeric IDs to UUIDs without client cache eviction.
Fix: Treat as breaking; bump cache key prefix or force app refresh.
Deploy order in federation
Symptom: Router fails composition or queries error on @requires fields.
Cause: Subgraph B deployed before subgraph A exposing new @external field.
Fix: Compose in CI; use composition checks; deploy router-compatible subgraph sets together.
Federation composition breaks
Symptom: CI blocks publish: entity key conflict or interface mismatch.
Cause: Two teams defined @key differently on shared entity.
Fix: Align entity ownership; use schema checks in registry; document key fields in guild meetings.
Next: Securing GraphQL APIs.