cimplify
Testing harness

Contracts

The SDK and the backend agree on shapes by snapshotting both sides as JSON Schema and diffing on every CI run. The pipeline runs on every PR; drift breaks the build before it can reach a storefront.

The two sources of truth

SideSourceSnapshot
BackendServer-derived typescontracts/server/*.json
SDKzod schemas in @cimplify/sdk/testingpackages/sdk/contracts/ts/*.json

Pipeline

# 1. Backend emits schemas from its typed models
bun run contracts:emit:server
# → writes Brand.json, Cart.json, CartItem.json, AddItemPayload.json,
#         CheckoutBody.json, CheckoutResponse.json, …

# 2. SDK emits schemas from zod via z.toJSONSchema()
bun run contracts:emit
# → writes packages/sdk/contracts/ts/*, same filenames

# 3. Diff each pair; CI fails on any structural difference
bun run contracts:diff

What the snapshot contains

Each emitted file is a standard JSON Schema with $id, description, version, and a $defs section for shared sub-shapes. Identical filenames on both sides are considered the same contract; that's how the differ pairs them.

packages/sdk/contracts/ts/
AddItemPayload.json
Brand.json
Cart.json
CartItem.json
CartPricing.json
CheckoutBody.json
CheckoutResponse.json
VariantInfo.json
manifest.json   # version pins per schema

manifest.json

The manifest pins each schema's version. Per-schema versioning lets a consumer pinned to an old SDK detect drift only on the contracts they care about, instead of a global SCHEMA_VERSION bump that false-positives on every release.

{
  "generated_at": "2026-05-07T13:55:55Z",
  "schema_count": 8,
  "schemas": [
    { "name": "Brand",            "file": "Brand.json",            "version": "1.0.0" },
    { "name": "AddItemPayload",   "file": "AddItemPayload.json",   "version": "1.0.0" },
    { "name": "Cart",             "file": "Cart.json",             "version": "1.0.0" },
    { "name": "CartItem",         "file": "CartItem.json",         "version": "1.0.0" },
    { "name": "CheckoutBody",     "file": "CheckoutBody.json",     "version": "1.0.0" },
    { "name": "CheckoutResponse", "file": "CheckoutResponse.json", "version": "1.0.0" }
  ]
}

Drift failure example

Suppose a backend PR renames billing_address to billing on CheckoutBody. The server snapshot updates; the zod schema in the SDK doesn't. CI prints:

 contract drift: CheckoutBody.json
  + properties.billing
  - properties.billing_address
  ! version pin 1.0.0 bump required

build failed (contract drift)

Resolution path: update the SDK's zod schema, bump the per-schema version in both manifests, ship the SDK release, then merge the backend PR. The lockstep flow keeps live storefronts compatible with the backend they were built against.

Local diff

# From the SDK package, emit + diff in one step
bun run contracts:check

# Or from the workspace root, alongside the backend
bun run contracts:emit:server && bun run contracts:diff

Why JSON Schema

  • Both sides have first-class generators (backend schema derive, zod 4's z.toJSONSchema()); no hand-rolled DSL.
  • Diffable as text; reviewers see exactly which field, type, or constraint moved.
  • Downstream tooling (OpenAPI generators, agent prompt assemblers, codegen) can consume the same artefacts.

Next

  • Schemas The zod side of the contract

  • Suites Runtime checks that pair with the snapshots

On this page