One schema is the source of truth. A change that would break a client fails in our editor, at compile time — never in production, in front of a user.
Most teams describe their API more than once. The shape lives in the database, then again as a hand-written type on the server, again as a query string on the web client, again as an interface in the mobile app. Every copy is a place for the truth to drift, and the drift is silent: the day the database changes, the stale copies still compile, still ship, and surface as a bug in front of a user weeks later.We keep a single source of truth — the data schema — and generate everything downstream from it. The schema produces the API definition; the API produces typed query documents; those compile into one shared SDK that every client imports. No client writes a raw API string or hand-maintains a type. When the schema changes, the chain regenerates and every consumer moves with it. We extend this to the identifiers themselves: primary keys are typed values, not loose strings, so an order ID and a user ID cannot be transposed in a function call without the compiler objecting.The payoff is that a breaking change cannot hide. Remove a field the mobile app still reads and the mobile app stops compiling — on our machine, before the change ships, at the exact line that depended on the thing that moved. What would otherwise be a production incident becomes a red underline a developer clears in under a minute. The discipline costs a little up front — you commit to generation instead of hand-editing — and it retires an entire category of defect permanently.
In practice: changing one column triggers the chain — the API schema regenerates, the SDK rebuilds, and any of four client surfaces that used the old shape fail their type-check immediately. The fix is led by the compiler, not discovered by a user filing a ticket.