{schema-driven.dev}
Prove it

Schema-Driven Development

A manifesto

If you've ever written the schema so your agent could generate everything around it, you're already doing this. Schema-driven development is the name for treating that decision as the durable standard — and moving meaning into its own layer.

The standard

Six principles

  1. Define a shape once — Every shape that crosses a boundary is declared exactly once.
  2. Reuse before you define — Resolve a shared definition before writing your own.
  3. Define before you generate — Declare the shape first; then write the code to use it.
  4. Version definitions like code — Schemas change through proposals and semver — reviewed, diffed, attributable.
  5. Separate shape from meaning — The author declares what data is; what it means is added by everyone who uses it.
  6. Derive everything — Everything that carries the data — storage, APIs, validation, forms, docs, agent context — is derived from its definition.
Scope

Where this applies

Schema-Driven Development is for the data shapes that cross a boundary — between services, storage, the UI, and your agents. A throwaway script's local struct doesn't need a schema; the User five systems share does.

It begins where disagreement becomes expensive.

Not a schema registry, a serialization-format war, Big Design Up Front, or a replacement for specs and tests — a set of principles for deciding and sharing the shapes that matter.

The asymmetry

We version our code. We reinvent our data shapes.

We spent decades making code shareable: version control, packages, semver, review, dependency graphs. We version it, review it, publish it, depend on it.

Data shapes never grew the same habit. The formats exist — but none became the shared commons for data that packages are for code. User, Order, Address, Invoice — the structures every system runs on — get reinvented inside each codebase, a little differently every time. The one thing every system has to agree on is the one thing we never learned to share. Not the records — the shapes. What a User is, not who your users are.

Code can be rewritten. A disagreement about what a User is cannot be patched around forever — that isn't bad code, it's broken software.

Decide what things are. Generate the rest.

This debt was always there; agents just made it impossible to ignore. They generate almost everything now — so the only lasting leverage is what you decide the data actually is.

Agent-built systems do not fail at syntax. They fail at agreement. Your agents have no shared memory across sessions and your team has no shared context window. A schema is the single place where that agreement can live, be versioned, and be deterministically verified.

Nobody wants a schema registry. Everyone wants their next change to not silently contradict the last one.

“Your codebase has five definitions of User. Your agent wrote four of them.”

The practice

Schema-Driven Development

It's the practice of deciding data shapes before generating code. In the agent era, that practice needs a second layer: shared, typed context around what those shapes mean.

A practice in the TDD / DDD lineage. Schemas before code. Pin down the nouns; let agents write the verbs.

What we value

We value

  • a resolved definition over a convenient local copy
  • deciding the shape over writing the code (the code is cheap now)
  • one shape, many authors of its meaning over one owner of both
  • versioned disagreement over silent drift

There is value on the right — speed, autonomy, a single owner. We value the left more, because the left compounds and the right doesn't.

The economics

What one schema removes

Agents make every undecided data shape cheap to create and expensive to live with.

Point your codebases and its agents at one referenceable schema, and a whole category of busywork stops being generated in the first place:

  • Duplicate forms and validators for a shape that was already defined once
  • Hand-written mapping layers between services, storage, and agents that each read the shape a little differently
  • Storage drift, and the migrations that come to mop it up
  • Re-explaining the same field to every fresh agent session and every new teammate

The generation is cheap. The coordination and translation work is what compounds.

Objections

What people might push back on

Questions that people may have who are unfamiliar with schema-driven development.

Isn't this just advocating for a schema registry?
A registry answers one question: is this payload the right shape? That's table stakes, and it's decades old: it validates, versions, stores. What it doesn't carry is the thing that actually drifts — what a field means once it crosses a boundary, who decided that, whether you should trust them. Two teams can both pass the same registry's validation and still disagree about whether a field is PII, a migration artifact, or safe to render raw. Shape already has a home. Meaning doesn't. A registry can host that layer; it doesn't constitute it, the way a filesystem can hold a database without being one.
Not another .md file in the repo root.
There are too many already: AGENTS.md, CLAUDE.md, .cursorrules, llms.txt, most of them config your tool reads at startup. SCHEMA.md isn't config. It's a claim about the repo — here is where data shapes get decided, for every human, agent, and CI job that opens the project. The file on its own changes nothing. What changes behaviour is the one line you add to AGENTS.md or CLAUDE.md pointing at it; that line gets read every session. Leave it out and SCHEMA.md is llms.txt all over again: present, unread, decorative.
How is this different from spec-driven development?
A spec describes tasks as tests, not at runtime. A schema describes the world the data that the spec is trying to manipulate. The schema shapes still standing three sessions later, the thing everything agrees on. A schema is never done; it just gets versioned. The two don't compete. You'll write specs on top of schemas all day, and the schema is the part that doesn't expire.
Schema-first already failed — SOAP, WSDL, design-first OpenAPI. Why now?
It failed twice, and both times for a real reason. First, deriving was manual: every WSDL or design-first spec meant stubs, mappers, and docs maintained by hand, and they drifted the moment anyone got busy. Second, and this is the one people forget, nothing was ever really shared. 'I publish the contract, you consume it' still meant your codebase generated its own copy of my types and wandered off, so you wrote the translation layer anyway. Agents kill the first problem: derived code is free now, so regenerate it and stop maintaining it. A genuinely shared definition kills the second: your client resolves my schema instead of forking a copy. What's left is deciding what the thing is, which was always the part worth a person's time. And the honest unknown stays unknown — whether shared definitions actually hold at ecosystem scale is unproven, and SOAP bet the house on exactly that and never earned it. What's different this time is the cost structure underneath, not the conviction.
More objections →
The chain

Reuse before you define.

Define before you generate.

Derive everything.

Terms
shape
the structure of a piece of data: its fields and their types.
schema
a shape declared on purpose, in one place, that humans and agents resolve alike.
context
typed meaning attached to a definition: what a field implies, who masks it, how long to keep it. Authored by its users, not its owner.
derived artifact
anything produced from a schema (storage, API, validation, forms, docs, agent context); generated or hand-written, it answers to the schema.
resolve
to look up and use a shared definition by reference, instead of redeclaring your own.
Try it

See where your repo stands

Try this to see where your code is at. No risk, read-only — one paste into your agent.

paste into your agent
Fetch https://schema-driven.dev/llms.txt to learn the standard, then work
in three phases. Don't write anything in phase 1; don't start phase 2 until
I say yes; don't declare a schema in phase 3 until I've picked it.

Phase 1 — explore (read-only). Don't create, edit, or generate anything.
Map the schemas I already have but can't see: the data shapes that live in
the code with no single declared definition. Look for —

1. Shapes defined more than once: a User, an Address, an Order redeclared
   across types, validation, database models, API payloads, and the
   context I hand my agents — each copy a little different.
2. Shapes that cross boundaries: the structures that travel between UI,
   validation, storage, and agent context with no one place that says
   what they are.
3. Shapes my agents keep re-inventing: the definitions a coding agent has
   to guess at because nothing authoritative exists to resolve.

Report an inventory: name each hidden schema, list every place it's
redefined today, and show where the copies have already drifted apart.
Rank them by how much a single declaration would remove. Change nothing —
this is a map, not a migration. Then ask me: "Want to make this repo
schema-driven?"

Phase 2 — adopt. Only if I say yes, and reusing the inventory from phase 1
(don't re-scan):

1. Create a /schemas directory in the project root — the single source of
   truth for every data shape in the repo.
2. Write a SCHEMA.md in the project root. Declare the repo schema-driven,
   point to /schemas as the authoritative state, and state the rule: no
   code is generated for a data type before its schema is proposed and
   approved.
3. Add the rule to your agent: locate CLAUDE.md, AGENTS.md, or
   .cursorrules (create CLAUDE.md if none exist) and add this line at the
   very top: "IRON RULE: Before creating, modifying, or generating code
   for any data type, you must read SCHEMA.md first."

The repo is now schema-driven — but nothing is converted yet.

Phase 3 — declare the first schema. Bring back the ranked map from phase 1
and recommend which shape to hit first: the one declaration that removes
the most pain. Tell me why, and wait for me to choose — don't convert
anything on your own. Once I've picked, define that one schema as a strict
JSON Schema (json-schema.org, draft 2020-12) under /schemas, then add a
script that derives its types and validation from that schema — proposing
the definition for me to approve before any code generates against it.
Then stop, and we choose the next one together.

Then watch it work. Ask your agent to add a phone number to signup, and watch it propose a schema first. Then ask it to reuse an Address you defined in another repo, or to note that a field is masked in logs somewhere that outlives the session — and watch it run out of places to look.