Your JSON Schema Answers One Question. You Keep Asking It Four.
The review comment that starts the trouble is always polite. *"Quick question — is this `customer_id` the same one the warehouse calls `customer_key`?"* The pull request adds two properties to a JSON Schema file. The author is fairly sure the answer is yes. Nobody can prove it from what is on the screen, because what is on the screen is a validator, and validators do not carry that kind of knowledge. The reviewer approves. The question stays open, and the next person to ask it will be an on-call engineer at an inconvenient hour.
Your JSON Schema Answers One Question. You Keep Asking It Four.
The review comment that starts the trouble is always polite. "Quick question — is this customer_id the same one the warehouse calls customer_key?" The pull request adds two properties to a JSON Schema file. The author is fairly sure the answer is yes. Nobody can prove it from what is on the screen, because what is on the screen is a validator, and validators do not carry that kind of knowledge. The reviewer approves. The question stays open, and the next person to ask it will be an on-call engineer at an inconvenient hour.
JSON Schema earned its adoption. It is easy to write, trivial to validate against, and it lives happily in the repository next to the code it describes. It answers one question superbly: does this document conform? The trouble is that teams quietly ask it three or four more — and it was never built for those.
Question two: what does this field actually mean?
A schema can tell you that status is a string restricted to four values. It cannot tell you which team owns that list, whether closed means cancelled or fulfilled, or that the same concept exists in the warehouse under a different name. You can write a description, and you should, but nothing consumes it and nothing keeps it consistent with the four other files that also define status. Validation is airtight; meaning is folklore passed between engineers who happen to have been in the room.
Ask "which fields across our estate represent the same real-world thing?" and a folder of schema files offers nothing but string similarity. customer_id, customerId, cust_key, party_ref — four names, and the file cannot tell you whether they are one concept or four.
Question three: how many copies of this are there?
Because schemas live beside code, every service that touches "customer" grows its own definition. They start identical. Then one team adds a field for a feature flag. Another loosens a required list at 4pm on a Thursday to unblock a release. A third trims an enum because a downstream consumer choked on a value nobody uses anymore.
Nothing forces convergence, because there is nothing to converge on. The result is not five versions of one model; it is five models that share a name. The differences stay invisible until a payload one service blessed gets rejected by another, and someone spends an afternoon diffing JSON to discover that a required array grew a member eight months ago.
Question four: what is this in the warehouse, the topic, the contract?
The schema never stays a schema. Somebody hand-writes the CREATE TABLE for the warehouse. Somebody else writes the Avro record for the streaming platform, a data contract for a partner, a proto file for an internal service, documentation for everyone.
Each of those translations is done once, by hand, under deadline — and from that instant it drifts on its own timeline. When the JSON Schema changes, no mechanism carries the change into the DDL or the contract. There is only the hope that the person who edits one remembers the other four exist. And translation is where meaning quietly dies: the enum that became a bare VARCHAR because the target dialect has no inline enum construct, the date-time string that landed as plain text, the anyOf that a code generator flattened to "string" without saying a word. Nobody logged those decisions.
Why the usual workarounds don't hold
Code generators go one direction, for one pair of formats, and stop; chain three and you are debugging a compiler pipeline nobody owns. A JSON diff in a pull request shows that text changed, not that meaning changed — a reordered required array and a narrowed enum look about the same at a glance. And documentation stays correct for exactly as long as it takes the first hotfix to land.
What we built for it
CoreModels imports JSON Schema — format key jsonschema, with sia accepted as an alias — into a governed model, and exports it back out. The import is structural, not textual. An object becomes a Type. Each property becomes an Element. A required entry becomes a required flag. An enum becomes a Taxonomy with named terms you can point at. A $ref becomes a real reference between types. An array becomes a cardinality carrying its minItems and maxItems. The allOf-over-a-$ref idiom becomes inheritance. Definitions under $defs become named types in their own right. What was a wall of nested JSON becomes something you can browse, own, and change in one place.
Nothing you wrote is discarded on the way in. Keywords the model does not represent structurally — your $schema dialect declaration, description texts, format hints, string constraints, your own extensions — are preserved verbatim and re-emitted exactly on export. Our test suite asserts that a clean schema makes the full round trip exactly, with an empty lossiness report; that is a test, not a slogan.
And where the model genuinely cannot follow, we say so out loud. The conditional combinators — anyOf, oneOf, if/then/else, not — cannot be represented as structure. The importer records a lossiness entry naming the exact path and explaining why, while still preserving the raw JSON so the export re-emits it untouched. You are told precisely what could not be lifted into the model, and you still lose nothing from the document.
The point
Once the model lives in one place, the multiplication problem inverts. There is one definition of customer, with meaning attached to it — an x-maps-to annotation binding a field to a term everyone can look up, not a description nobody reads. The JSON Schema your services validate against becomes something you regenerate, not something you maintain. So does the Postgres, MySQL, or SQL Server DDL, the Avro record, the LinkML model, the proto3 file, the ODCS contract. Every one of those exports arrives with an honest report of what the target format could not hold exactly, so your translations stop being silent forks and start being reviewable derivations.
JSON Schema is very good at the job it was designed for. The pain was never validation. The pain is that a validation format got promoted into a modeling tool, and modeling is precisely what it does not do. We are not asking you to give it up — we are giving it the home, the meaning, and the honesty it never had.
To watch a round trip run on one of your own schemas, start with the schema transformation quickstart in the CoreModels docs.