Why is adding a value to a GraphQL enum risky for readers while removing one breaks senders?
answer
- Which side chooses the value?
- Readers and senders break in opposite directions
- Every old document still validates
- Server metrics stay green throughout
- Closed set in schema, open on the wire
basics
~10 sIn output position the server picks the value, so a new member reaches readers whose code maps only the values they knew. In input position the client picks, so removing a member rejects senders.
solid answer
~50 sAn enum is a closed set in the schema, but the two directions carry opposite risk. In **output** position the server chooses the value: adding `SCREEN_FAILED` to `EnrollmentStatus` leaves every deployed document valid — a document selects `status`, it never lists the values — yet a client that maps each known value to a UI state now meets one it cannot map. In **input** position the client chooses: removing a value breaks every caller that still sends it, as a rejected request, while adding one is harmless because nobody sends what they do not know. An enum used in both positions therefore has no safe direction. The tell during an incident is that server-side signals stay clean — validated, executed, well inside a 340 ms p99 budget — because the failure lives entirely in the caller. Treat output enums as open sets and require clients to carry a fallback branch.
code
graphql · 6 linesenum EnrollmentStatus {
SCREENING
ENROLLED
WITHDRAWN
SCREEN_FAILED
}go deeper
Know that an enum is a fixed set of named values and that a client can only send values the schema declares. Remember the headline: adding a value is safe for senders, removing one is not.
Explain why the direction matters — who chooses the value — and why an added output value leaves documents valid while still breaking a caller whose code maps every known value.
Show the diagnostic instinct: a client failure that starts at a schema deploy with flat server metrics points at values, not resolvers. Then state the prevention on both sides, server policy and client fallback.
Own the modelling call: whether an axis that keeps growing should be an enum at all, and whether unknown-value tolerance is an obligation you publish and enforce on every consumer of the graph.
## The same enum, two opposite risks An enum in the schema is a closed set of named values. What makes enum evolution a classic trap is that the risk flips depending on **which side chooses the value**. **Output position — the server chooses.** `Enrollment.status: EnrollmentStatus` means the server may return any member of the set. A client's document does not list the values; it just selects `status`. So adding `SCREEN_FAILED` to the enum leaves every deployed document valid, and the server has no way to notice a problem. But the reader has code shaped like a total mapping: value to label, value to colour, value to which buttons are enabled. A value outside that mapping is at best an empty screen and at worst a thrown exception. **Input position — the client chooses.** `enrollParticipant(status: EnrollmentStatus)` means the caller supplies a value it hard-coded when it was written. Adding a value is therefore harmless: nobody sends what they do not know about. Removing one is the breakage, and it is a document-level breakage — the literal no longer validates, or the variable no longer coerces, and the whole request is rejected. **Both positions — no safe direction.** An enum used as both an output field type and an argument type has no edit that is safe in general. Adding endangers readers; removing endangers senders. That is a good reason to think twice before reusing one enum on both sides of the graph. ## An incident that reads like a write bug A clinical-trial registry adds `SCREEN_FAILED` to `EnrollmentStatus` so sites can record participants who did not pass screening. The schema edit is reviewed as additive and ships on a Tuesday. The site-facing client calls `enrollParticipant` and then renders the returned enrollment. Its status mapping covers `SCREENING`, `ENROLLED` and `WITHDRAWN`. On the first participant marked screen-failed, the mutation succeeds, the row is written, and the client throws while rendering the response. The operator sees an error toast, assumes the enrollment did not save, and clicks *Enroll* again — a duplicate write from a retry, and the registry now holds two enrollment rows for one participant. What makes this hard to diagnose is that every server-side signal is clean. The document validated. Execution succeeded. The mutation sat comfortably inside its 340 ms p99 budget. Error rates, resolver traces and the errors array in the response all show nothing, because there was nothing wrong with the response — the schema promised a value from `EnrollmentStatus`, and it delivered one. The break is entirely in the caller's mapping, one hop past anything the server can see. The tell, when you are on the call: a client-side failure that starts precisely at a schema deploy, with server metrics flat, and a payload containing a string nobody recognises. Ask what enum values shipped, not what resolvers changed. ## The prevention, in two halves **Server side: treat an output enum as an open set.** Adding a value is a change of behaviour visible to every reader, so it belongs in the same review conversation as removing a field, even though the tooling and the validator both call it safe. If a set is genuinely volatile — new statuses every quarter — an enum is pushing your churn onto every reader, and it is worth asking whether that axis should be modelled some other way, accepting the loss of a validated closed set in exchange for never surprising a reader. **Client side: require a fallback branch.** A caller must be able to receive a value it has never heard of and do something defined: render the raw value, fall back to a neutral state, log it. Generated clients differ in what they do here — some map an unrecognised value to a catch-all member, some produce a closed union that simply fails to match, some throw while decoding — so this is a property to verify in your own stack rather than assume. In the registry incident, a fallback that rendered the unknown status as plain text would have turned a duplicate-write incident into a cosmetic one. ## Where this differs from the spec None of this is a specification rule. The GraphQL specification defines what an enum is, how values coerce, and that a value outside the set is invalid input; it says nothing about whether adding a member is a "breaking change", because that phrase is about *your* consumers, not about validity. Schema-diff tooling generally flags an added output-enum value as risky rather than fatal for exactly this reason: it can see that documents remain valid, and it cannot see the mapping inside your client.
- What exactly do you require of a client so an added output enum value is survivable?A defined fallback for a value it does not recognise: render the raw string, fall back to a neutral state, log it — anything but throw. Generated clients differ here; some map an unknown value to a catch-all member, some emit a closed union that simply fails to match, some fail while decoding. Verify which yours does rather than assuming, and write a test that feeds it an unknown value.
- Is an enum ever the wrong choice for a set you expect to keep growing?Yes. A schema enum pushes every addition onto every reader, so a set that grows each quarter is exporting your churn. When values are open-ended, modelling the axis as a scalar loses the validated closed set and the generated type, but it never surprises a reader. Reserve the enum for sets that are genuinely stable, and keep the volatile part separate.
- Does adding a value to an enum used only as an argument type break anyone?No. Callers send values they hard-coded when they were written, so a value they have never heard of cannot appear in their documents, and every existing document still validates and coerces. The risk only arrives when that same enum is also used as the type of an output field, at which point additions become a reader problem.
An output enum is the set of postcards you might mail; an input enum is the set of addresses you accept. A new postcard design surprises the reader, while dropping an accepted address turns senders away.
saying these in an interview costs you the question
- Says adding an enum value is always additive and safe
- Thinks removing an enum value only affects readers
- Assumes clients tolerate unknown enum values by default
- Rules out breakage because server error rates are flat
- Calls an enum safe because documents never list its values