| name | schema-driven-development | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| description | Schema-Driven Development (SDD) - JSON Schema Draft-07 is an Object DSL. SDD uses it to write class definitions (state + behavior + test intent + semantic context), then derives everything else. Covers x- extensions, runtime validation, contract testing, CI/CD automation. | ||||||||
| version | 1.0.0 | ||||||||
| tags |
|
TL;DR: JSON Schema Draft-07 is an Object DSL — type system, logic, composition, control flow. The ecosystem reduced it to a validator config. SDD uses the full DSL to write class definitions. Everything else (types, tests, docs, validation) is derived from it.
JSON Schema = Object DSL. SDD writes class definitions in it. Change the schema, derive everything else.
Use when the task involves defining a data entity, API contract, or domain object.
Triggers:
- User describes a new entity ("we need a User model", "define an Order")
- User asks for an API contract or data schema
- User wants single source of truth across languages
- User mentions schema-driven, schema-first, or SDD
- Task requires shared types between frontend/backend/services
JSON Schema Draft-07 is a DSL with four elements:
Type system → type, format, enum, const, minimum, maximum, pattern
Composition → properties, items, definitions, $ref
Logic → allOf (AND), anyOf (OR), oneOf (XOR), not (NOT)
Control flow → if / then / else, dependencies
The ecosystem used this DSL only for validation — one consumer. SDD uses it to write complete class definitions:
Draft-07 native → state (properties) + method body logic (allOf, oneOf, if/then)
SDD adds → x-methods (method namespace)
x-tests (test namespace)
x-docs (semantic namespace)
Result → one JSON document = one class definition
Toolchain and LLM both read the same document. No translation layer. All derived artifacts (types, tests, docs, validation) come from this single DSL source.
References:
- Philosophy:
methodology/sdd-philosophy.md - x- extension spec:
methodology/x-extensions.md
Six phases. Each phase specifies what the agent does, what it reads, what it produces.
PERCEIVE → SCHEMA → VALIDATE → DERIVE → IMPLEMENT → VERIFY
↑ ↓
←──────────────── (on failure) ─────────────────────←
Goal: Understand what class the user needs.
Agent reads:
- User request (feature description, API endpoint, data model)
- Existing schemas in
schemas/(find related classes) - Existing codebase patterns (naming conventions, shared definitions)
Agent produces:
- Entity name (e.g.,
User,Order,Event) - Field list with types and constraints
- Business rules that govern the entity
- Relationships to other entities (
$refcandidates)
Decision gate: Confirm entity scope with user before proceeding.
Input: "We need a user entity with email login and role-based access"
Output: Entity=User, fields=[email(string,email), age(integer,>=0), roles(array,enum)],
rules=[email is canonical ID, roles non-empty, age optional]
Goal: Write the complete class definition in the Object DSL.
Agent writes one JSON file with four sections:
- State —
properties+required+ constraints - Behavior —
x-methodswith Draft-07 logic bodies - Test intent —
x-testswith scenario tags per method - Semantic context —
x-docswith intent, category, notes
Completeness rule: A schema without all four sections is incomplete. Do not proceed.
Agent actions:
- Create
schemas/<entity>.schema.json - Write
propertieswith type constraints (type,format,minimum,pattern, etc.) - Write
x-methods— each method body is valid Draft-07 logic - Write
x-tests— select from standard scenario tags - Write
x-docs— object-levelintent+category+notes, field-level where non-obvious - Use
$reffor shared definitions and cross-schema composition
Standard scenario tags (for x-tests):
| Tag | Meaning |
|---|---|
valid_normal |
typical valid case |
valid_edge |
valid but unusual |
null |
field is null |
missing |
required field absent |
empty |
empty string / array |
min_boundary |
exactly at minimum |
max_boundary |
exactly at maximum |
below_min |
one step below minimum |
above_max |
one step above maximum |
invalid_format |
wrong format |
Output example:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "User",
"type": "object",
"properties": {
"email": {"type": "string", "format": "email"},
"age": {"type": "integer", "minimum": 0},
"roles": {"type": "array", "items": {"enum": ["admin", "user", "guest"]}, "minItems": 1, "uniqueItems": true}
},
"required": ["email", "roles"],
"x-methods": {
"is_adult": {"properties": {"age": {"minimum": 18}}},
"is_admin": {"properties": {"roles": {"contains": {"const": "admin"}}}},
"has_complete_profile": {
"allOf": [
{"required": ["email", "age", "roles"]},
{"properties": {"age": {"minimum": 0}}}
]
}
},
"x-tests": {
"is_adult": ["min_boundary", "below_min", "null", "valid_normal"],
"is_admin": ["valid_normal", "valid_edge", "missing"],
"has_complete_profile": ["valid_normal", "missing", "valid_edge"]
},
"x-docs": {
"intent": "Core user entity for authentication and authorization.",
"category": "auth",
"notes": "Email is immutable after creation. Role changes require audit log."
}
}Goal: Confirm the schema is syntactically valid and semantically complete.
Agent checks:
-
Syntax — valid Draft-07 JSON Schema
ajv compile -s schemas/<entity>.schema.json
-
Completeness — all four sections present
propertieswith constraintsx-methodswith Draft-07 logic bodiesx-testswith scenario tags for every methodx-docswithintentat minimum
-
Consistency — cross-reference check
- Every method in
x-methodshas an entry inx-tests - Every
$refresolves to an existing definition or file - Field names in
x-methodsbodies exist inproperties - Scenario tags match field constraints (e.g.,
min_boundaryonly whereminimumexists)
- Every method in
-
Examples — validate sample data against the schema
ajv validate -s schemas/<entity>.schema.json -d examples/<entity>.valid.json
On failure: Return to Phase 2 with specific errors.
Goal: Generate all derived artifacts from the schema.
The schema is the single source of truth. Everything else is derived.
Agent derives:
| Artifact | Source | Tool |
|---|---|---|
| Language types | properties + required |
Type generator (per language) |
| Validation logic | properties + constraints |
Schema validator library |
| Test cases | x-tests + properties constraints |
Test generator |
| Documentation | x-docs + properties + x-methods |
Doc generator |
| API spec | Schema + endpoint mapping | OpenAPI bundler |
Derivation rules:
- Types are generated, never hand-written
- Validation uses the schema directly at runtime (no code generation)
- Test cases combine scenario tags with field constraints to produce concrete test data
- Documentation extracts
x-docs.intentfor summaries,x-docs.notesfor caveats
Test derivation example — from x-tests + properties:
x-tests.is_adult: ["min_boundary", "below_min", "null", "valid_normal"]
properties.age: {"type": "integer", "minimum": 0}
x-methods.is_adult: {"properties": {"age": {"minimum": 18}}}
Derived test cases:
min_boundary → {"age": 18} → method passes
below_min → {"age": 17} → method fails
null → {"age": null} → method fails
valid_normal → {"age": 30} → method passes
Goal: Write application code that consumes the schema as an object.
Agent actions:
- Load schema at runtime — the schema is the program, not a config file
- Instantiate as object — use
ObjectTree(Python) orSchemaValue(Rust) - Use Draft-07 logic as methods —
validate(),one_of(),if_then(),project() - Read x-methods —
get_extensions()returns x- definitions, runtime logic is yours - Validate at exchange boundaries — request in, response out, never in business logic
Implementation pattern (Python):
from schema2object import ObjectTree
import json
schema = json.load(open("schemas/user.schema.json"))
data = {"email": "alice@example.com", "age": 30, "roles": ["admin"]}
user = ObjectTree(data, schema=schema)
# State access
user.email # "alice@example.com"
# Draft-07 logic as methods
user.validate() # constraint enforcement
clean = user.project() # keep only schema-defined fields
# x-methods — read definition, runtime logic is yours
user.get_extensions() # {'x-methods': {'is_adult': {...}}, 'x-docs': {...}}Implementation pattern (Rust):
let user = SchemaValue::new(data, schema.clone());
user["email"]; // index access
user.validate()?; // constraint enforcement
user.one_of()?; // XOR dispatch
user.project()?; // SELECT schema fields
// x-methods — read definition, runtime logic is yours
// schema["x-methods"]["is_adult"] → use as you see fitExchange boundary validation:
Request → validate against schema → reject if invalid
↓ (valid)
Business logic → no validation needed (schema guarantees structure)
↓
Response → validate against schema → fail loud if invalid (bug in our code)
Goal: Confirm all derived artifacts are correct and the system works end-to-end.
Agent checks:
- Schema validates —
ajv compilepasses - Examples validate —
ajv validateagainst sample data passes - Derived tests pass — run generated test cases
- x-methods evaluate correctly — each method produces expected result for test data
- Exchange boundaries hold — valid data accepted, invalid data rejected
- No drift — generated types match schema, docs match schema, tests match schema
Verification sequence:
# 1. Schema syntax
ajv compile -s schemas/*.json
# 2. Examples
ajv validate -s schemas/user.schema.json -d examples/user.valid.json
# 3. Tests
cargo test # Rust
python -m pytest # Python
# 4. Lint
spectral lint schemas/**/*.jsonOn failure: Identify which phase broke, return to that phase.
Test fails on is_adult → check x-methods body → Phase 2 (fix schema)
Type mismatch → check derivation → Phase 4 (re-derive)
Boundary rejected → check validation logic → Phase 5 (fix implementation)
Patterns the agent uses when building multi-entity systems.
{
"allOf": [
{"$ref": "base-resource.schema.json"},
{
"properties": {
"email": {"type": "string", "format": "email"}
}
}
]
}{
"oneOf": [
{"properties": {"type": {"const": "admin"}, "permissions": {"type": "array"}}},
{"properties": {"type": {"const": "guest"}, "expires_at": {"type": "string", "format": "date-time"}}}
]
}{
"properties": {
"author": {"$ref": "user.schema.json"},
"tags": {"type": "array", "items": {"$ref": "tag.schema.json"}}
}
}project/
├── schemas/ # Class definitions (single source of truth)
│ ├── user.schema.json
│ ├── order.schema.json
│ └── common/
│ └── base-resource.schema.json
├── examples/ # Validation data
│ ├── user.valid.json
│ └── user.invalid.json
└── tests/ # Derived from x-tests
└── contract/
Before moving to implementation, the agent verifies:
- Schema has
propertieswith type constraints - Schema has
x-methodswith Draft-07 logic bodies - Schema has
x-testswith scenario tags for every method - Schema has
x-docswithintentat minimum - Every
$refresolves -
ajv compilepasses - Examples validate against schema
- No hand-written types — all derived from schema
The workflow:
PERCEIVE → SCHEMA → VALIDATE → DERIVE → IMPLEMENT → VERIFY
Each phase is agent-executed. The schema is the program. Everything else is derived.
| Phase | Agent does | Produces |
|---|---|---|
| PERCEIVE | Read user request + existing schemas | Entity scope, field list, business rules |
| SCHEMA | Write class definition in Object DSL | .schema.json with properties + x-methods + x-tests + x-docs |
| VALIDATE | Check syntax, completeness, consistency | Pass / fail with specific errors |
| DERIVE | Generate types, tests, docs from schema | Language types, test cases, documentation |
| IMPLEMENT | Load schema at runtime, use as object | Application code using ObjectTree / SchemaValue |
| VERIFY | Run all checks end-to-end | Pass / return to failed phase |
References:
methodology/sdd-philosophy.md— JSON Schema as Object DSLmethodology/x-extensions.md— x-methods / x-tests / x-docs specreferences/quick-start.md— write a schema, use it as an object
- Domain entity:
examples/user-walkthrough/user.schema.json - MCP server (full working example):
templates/mcp-server/