Schemas define the boundary

Schema defines accepted values, runtime checks, and associated TypeScript types. Decoding checks external input and produces an application value. Encoding produces its external representation. A codec connects these two representations.

Define the values the application accepts before writing the handler. Decode unknown input at the boundary. Encode results through the declared output schema.

Use one model for types and runtime checks

Schema.Struct describes a record. Schema.Literals describes a fixed set of alternatives. A schema’s Type describes its decoded value; Encoded describes its external representation. Those representations can differ when a codec transforms values.

packages/core/src/contracts.ts · 65e9465f
const ContentKind = Schema.Literals(["law", "skill", "lore"]);

export const SearchMatch = Schema.Struct({
  description: Schema.String,
  digest: Schema.String,
  excerpt: Schema.String,
  id: Schema.String,
  kind: ContentKind,
  routePath: Schema.String,
  score: Schema.Finite,
  title: Schema.String,
});

Content kinds have three accepted values. A match must carry the declared fields and a finite score.

Derive the TypeScript type from the schema rather than maintaining a second interface. Use Schema.Union when accepted alternatives have different shapes, including tagged failures.

Put the schemas in the contract

A contract declares an action’s input, output, and failures. The file-inspection contract names its input, result, and failure.

packages/core/src/contracts.ts · 65e9465f
export const inspectFileContract = defineContract("inspectFile", {
  annotations: { idempotent: true, readOnly: true },
  description: "Count bytes, characters, words, and lines in a file",
  failure: FileStatsError,
  input: Schema.Struct({
    path: Schema.String.annotate({ description: "File to inspect" }),
  }),
  output: FileStatsSchema,
});

The schemas check values at runtime.

Decode before dispatch

Schema.decodeUnknownEffect accepts unknown input and keeps decoding failures in the Effect error channel. Our code-mode invoker calls the handler only after decoding succeeds.

packages/capability/src/to-code-mode.ts · 65e9465f
      return Schema.decodeUnknownEffect(item.contract.input)(input).pipe(
        Effect.matchEffect({
          onFailure: (error) =>
            Effect.succeed(invokeFailure("InvalidInput", error.message)),
          onSuccess: (decoded) =>
            run(decoded).pipe(
              Effect.provideContext(context),
              Effect.matchEffect({
                onFailure: (error) =>

Rejected input becomes InvalidInput. The domain handler receives the decoded value after validation.

The same invoker uses Schema.encodeUnknownEffect for declared outputs and failures. For an already-typed value, Schema.encodeEffect supplies the typed encoding API. JSON serialization alone does not check whether a result satisfies its contract.

A schema states what a value means

The interest contract separates contact consent from sharing consent. Accepting contact does not authorize sharing. Its answer fields are bounded strings.

AbuseVerdictSchema describes a hold flag, a finite score between zero and one, and signal names. The AbuseVerdict type derives from that schema. These are domain decisions; Schema does not choose them for us.

A generated input also does not supply a behavior assertion. Property tests18 still need an independent rule that those values must satisfy.

Common wrong approach

A type assertion changes what TypeScript believes. It does not reject a malformed external value. Decode at the boundary instead of casting it into a domain type.

Hand-written JSON parsing followed by a copied interface creates two models that can drift. Use the schema’s decoding and encoding APIs. Report malformed input separately from an expected domain refusal36.

Effect idiom and house rule

See also: one schema, three surfaces23, OpenAPI refusals15, services16, and testing22.

Sources

  1. Effect contributors. 2026. Schema. Effect 4.0.0.. Effect-TS. Struct, Literals, Union, schema-derived types, and effectful decoding and encoding. Accessed 2026-10-06.
  2. Effect contributors. 2026. Schema basics.. Effect-TS. Schema models expose Type and Encoded representations and reusable effectful parsers. Accessed 2026-10-06.
  3. rat-stack contributors. 2026. Shared capability contracts.. rat-stack. Contract schemas model input, output, failures, records, and literal alternatives. Accessed 2026-10-06.
  4. rat-stack contributors. 2026. Code-mode projection.. rat-stack. The invoker decodes input before dispatch and encodes declared outputs and failures. Accessed 2026-10-06.
  5. rat-stack contributors. 2026. Interest application contract.. rat-stack. Contact consent and sharing consent remain separate fields. Accessed 2026-10-06.
  6. rat-stack contributors. 2026. Abuse score schemas.. rat-stack. The verdict type derives from a schema with a finite bounded score. Accessed 2026-10-06.
  7. Langton, Kit. 2026. Data Modeling. Effect Solutions.. Kit Langton. Topic framing for records, alternatives, and schema-backed domain values; no chess example is copied. Accessed 2026-10-06.

Last changed 2026-10-07 in f1a00f2. Source on GitHub. Change log.

Linked from

  1. Agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.

    Schemas define the boundary — Define accepted data with Schema, decode unknown input before dispatch, and encode results through the declared contract.

  2. CAFE names the Cloudflare, Alchemy, Foldkit and Effect stack. CAFE is a named stack: Cloudflare runs the app, Alchemy declares it, Foldkit draws the UI, and Effect does the typed work.

    Recipe variants are typed by Effect Schema.¶

  3. Change log. What changed in the files served here, newest first.

    Schemas define the boundary — edited: feat(learn): teach cards 63 to 82 with plain lines, diagrams and snippets

  4. Code snippets quote a pinned source. Shipped design brief: pinned Git objects, typed diagnostics, and one build-only snippet model for both views.

    Schema decoding parses unknown metadata into a CodeRequest at the boundary.

  5. Effect basics. An Effect describes work, expected failures, and required services before a runtime executes it.

    Schemas define the boundary: define and decode accepted data.¶

  6. Effect keeps the surfaces on one contract. Schema owns values, projections build interfaces, and Layers supply implementations.

    Schemas define the boundary: decode unknown values and keep domain refusals typed.¶

  7. Errors are part of the interface. An author error needs a location, the rejected input, and a repair.

    Use schema decoding to reject invalid input at the boundary.

  8. Feature flags. Typed feature flags with owners, removal dates, and stable subject rollouts.

    A flag declares its name, schema, default, owner, and removal date.¶

  9. Full agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
  10. Handle expected failures without hiding defects. Declare expected failures, preserve their context, and recover by tag without turning crashes into successful outcomes.

    See also: schemas, author-facing diagnostics, independent failures, and lifecycles.¶

  11. HTTP success needs a status check and a decoder. Supply the client, check the response status, decode accepted data, and name failures at the adapter boundary.

    schemaBodyJson handles JSON parsing and schema decoding.

  12. One capability, every surface. A schema-typed contract and one handler feed every interface rat-stack keeps.

    Michael Arnaldi demonstrates Schema decoding request input and encoding a frontend-safe result while preserving its output type.

  13. One schema, three surfaces. A capability contract feeds CLI, HTTP, and MCP; OpenAPI is generated output, not their input.

    ts derives flags or positional inputs, schema decoding, and the command handler.¶

  14. OpenAPI describes the refusal too. The generated HTTP contract declares inputs, outputs, and the bodyless 400 returned when decoding fails.

    See schema-driven development for that boundary.

  15. Rat Stack lore | rat-stack. Short, source-grounded notes on the ideas and decisions behind rat-stack.

    Schemas define the boundary — Define accepted data with Schema, decode unknown input before dispatch, and encode results through the declared contract.

  16. Source change history (log.md). The generated change log as Markdown.

    Schemas define the boundary — edited: feat(learn): teach cards 63 to 82 with plain lines, diagrams and snippets

  17. Structure an Effect codebase by domain. Start with folders for application work, services and Layers in the same file, and startup code that supplies implementations.

    Decode at boundaries with Schema.¶

Unlinked mentions (36)