Skip to content

Latest commit

 

History

History
437 lines (332 loc) · 14.1 KB

File metadata and controls

437 lines (332 loc) · 14.1 KB
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
schema-driven
api
json-schema
rust
contract-testing
tdd
ci-cd
workflow

Schema-Driven Development (SDD)

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.

When to Use This Skill

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

Core Concept: JSON Schema = Object DSL

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

Agent Workflow

Six phases. Each phase specifies what the agent does, what it reads, what it produces.

PERCEIVE → SCHEMA → VALIDATE → DERIVE → IMPLEMENT → VERIFY
    ↑                                                    ↓
    ←──────────────── (on failure) ─────────────────────←

Phase 1: PERCEIVE

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 ($ref candidates)

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]

Phase 2: SCHEMA

Goal: Write the complete class definition in the Object DSL.

Agent writes one JSON file with four sections:

  1. Stateproperties + required + constraints
  2. Behaviorx-methods with Draft-07 logic bodies
  3. Test intentx-tests with scenario tags per method
  4. Semantic contextx-docs with intent, category, notes

Completeness rule: A schema without all four sections is incomplete. Do not proceed.

Agent actions:

  1. Create schemas/<entity>.schema.json
  2. Write properties with type constraints (type, format, minimum, pattern, etc.)
  3. Write x-methods — each method body is valid Draft-07 logic
  4. Write x-tests — select from standard scenario tags
  5. Write x-docs — object-level intent + category + notes, field-level where non-obvious
  6. Use $ref for 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."
  }
}

Phase 3: VALIDATE

Goal: Confirm the schema is syntactically valid and semantically complete.

Agent checks:

  1. Syntax — valid Draft-07 JSON Schema

    ajv compile -s schemas/<entity>.schema.json
  2. Completeness — all four sections present

    • properties with constraints
    • x-methods with Draft-07 logic bodies
    • x-tests with scenario tags for every method
    • x-docs with intent at minimum
  3. Consistency — cross-reference check

    • Every method in x-methods has an entry in x-tests
    • Every $ref resolves to an existing definition or file
    • Field names in x-methods bodies exist in properties
    • Scenario tags match field constraints (e.g., min_boundary only where minimum exists)
  4. 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.


Phase 4: DERIVE

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.intent for summaries, x-docs.notes for 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

Phase 5: IMPLEMENT

Goal: Write application code that consumes the schema as an object.

Agent actions:

  1. Load schema at runtime — the schema is the program, not a config file
  2. Instantiate as object — use ObjectTree (Python) or SchemaValue (Rust)
  3. Use Draft-07 logic as methodsvalidate(), one_of(), if_then(), project()
  4. Read x-methodsget_extensions() returns x- definitions, runtime logic is yours
  5. 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 fit

Exchange 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)

Phase 6: VERIFY

Goal: Confirm all derived artifacts are correct and the system works end-to-end.

Agent checks:

  1. Schema validatesajv compile passes
  2. Examples validateajv validate against sample data passes
  3. Derived tests pass — run generated test cases
  4. x-methods evaluate correctly — each method produces expected result for test data
  5. Exchange boundaries hold — valid data accepted, invalid data rejected
  6. 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/**/*.json

On 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)

Schema Composition Patterns

Patterns the agent uses when building multi-entity systems.

Inheritance — allOf + $ref

{
  "allOf": [
    {"$ref": "base-resource.schema.json"},
    {
      "properties": {
        "email": {"type": "string", "format": "email"}
      }
    }
  ]
}

Polymorphism — oneOf dispatch

{
  "oneOf": [
    {"properties": {"type": {"const": "admin"}, "permissions": {"type": "array"}}},
    {"properties": {"type": {"const": "guest"}, "expires_at": {"type": "string", "format": "date-time"}}}
  ]
}

Cross-schema reference

{
  "properties": {
    "author": {"$ref": "user.schema.json"},
    "tags":   {"type": "array", "items": {"$ref": "tag.schema.json"}}
  }
}

Project Structure

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/

Checklist

Before moving to implementation, the agent verifies:

  • Schema has properties with type constraints
  • Schema has x-methods with Draft-07 logic bodies
  • Schema has x-tests with scenario tags for every method
  • Schema has x-docs with intent at minimum
  • Every $ref resolves
  • ajv compile passes
  • Examples validate against schema
  • No hand-written types — all derived from schema

Summary

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 DSL
  • methodology/x-extensions.md — x-methods / x-tests / x-docs spec
  • references/quick-start.md — write a schema, use it as an object

Templates

  • Domain entity: examples/user-walkthrough/user.schema.json
  • MCP server (full working example): templates/mcp-server/