We had one API input schema declared twice. A Zod schema validated requests on the npm package path. A hand-written JSON Schema advertised the same input on a serverless worker path. Same tool, two deployment targets, two copies of the truth, kept in sync by discipline alone.
Zod strips unknown keys by default. No error, no warning. Anything the schema does not recognize is deleted before your handler ever sees it. So when the two copies drifted, a documented field sent down the npm path was quietly removed in flight. The response looked like a success. The data was just gone. The exact same call against the worker path worked, because that copy of the schema knew the field.
A bug report came in after someone read both schemas side by side, carefully. It listed 6 drifted fields.
While fixing it I wrote the test that should have existed from the start: import both schemas, extract the key sets, assert they match. It failed with 13 fields. One of them was being silently stripped in production and nobody had reported it, because the failure mode is a success response. The other 12 existed only in the validator, so they were invisible to anyone reading the advertised schema.
The part that actually stung: the file had a comment saying both copies must be kept in sync. It had been there through all 13 drifts. Comments don't run.
What I keep from this:
- When the invariant is "two lists must match", never enumerate the drift by hand. A careful human read found less than half of it. Diff the lists in a test and let the failure tell you the scope.
- A validator that strips unknown keys turns schema drift into silent data loss. Treat it as an allowlist that deletes whatever it does not know.
- The two drift directions fail differently. A key missing from the validator is a runtime data-loss bug. A key missing from the advertised schema is merely undiscoverable. Report them separately.
- On any system with two deployment paths, "I tested it" means nothing until you say which path.
Top comments (0)