Zod Engineering
Use Zod as a runtime boundary adapter, not as a replacement for public-contract,
security, backend, or domain ownership.
Use When
- A JavaScript or TypeScript boundary needs runtime parsing, normalization, or
typed output.
- A repository selects or migrates Zod, including Zod 3-to-4 compatibility.
- A Zod schema, mapped validation error, generated JSON Schema, or performance
claim needs review or tests.
Boundaries
Parse unknown once at ingress; use parsed output afterwards; map it to plain
application or domain types. Independently validate outbound data that crosses
an untrusted or compatibility-sensitive boundary. Zod parsing is neither
authorization nor sanitization and does not own domain invariants.
For a cross-language wire boundary use:
unknown JSON/IPC → Zod boundary schema → plain wire DTO → backend-native deserializer/validator → backend domain type
and reverse it through a stable serialized envelope before a UI/application
parse. See the patterns and examples.
Workflow
- Inspect
package.json, the selected lockfile/package manager/runtime,
TypeScript version, existing validator and import style, framework adapter,
trust boundary, and installed Zod version.
- Preserve a suitable established validator or Standard Schema adapter unless a
migration is explicitly requested. Decide whether a public artifact, backend
contract, or boundary schema owns the contract before writing a schema.
- Decide missing versus
null, unknown keys, coercion, defaults,
transforms/codecs, numeric/date/byte representation, size/depth/cardinality
limits, and error exposure explicitly.
- Parse at the adapter, map inward, normalize errors at the adapter/render
layer, and test the observable boundary.
Core Rules
- Prefer schema-first contracts with
z.infer, z.input, and z.output; use
z.toZod<T>() only for type-first compatibility. Never maintain duplicate
interfaces and schemas that can drift.
- Compose object shapes or use
.safeExtend() rather than long .extend()
chains. Use strict discriminated branches for overlapping event or intent
unions.
- Use object getters for recursive objects; use
z.lazy() when deferred
non-object or mutually recursive shapes are clearer.
- Read patterns and examples before
selecting parse APIs, composition, error mapping, JSON Schema, or interop.
Errors And Security
Map structured issue codes and bounded paths to a stable application/API error
envelope. Do not expose ZodError, default library wording, raw input,
transformed values, full issues, or reportInput without an explicit sanitized
policy. z.treeifyError() and z.flattenError() belong only in a presentation
adapter.
Load security-review and
security-review-evidence for trust
boundaries; they own authorization, sanitization, resource limits, and safe
security evidence. Load internationalization-localization
for translated user messages.
Testing
Test accepted and rejected boundaries; optional/null/unknown-key policy;
transforms and input/output divergence; sync and async parsing; mapped error
stability; and generated-contract parity. Add bounded property tests only when
the repository already uses a suitable generator. The runnable oracle is
common patterns.
Load test-driven-development for an
implementation loop and testing-strategy for
coverage review.
Performance And Migration
Measure representative valid, invalid-with-diagnostics, boolean-validation,
schema construction/reuse, transform, depth, and async workloads before using
z.compile() or z.validate(). Upstream benchmarks are not local evidence.
Read the dated Zod 4.5 baseline before a
version-sensitive change or migration.
Routing
References
- Patterns and examples
- Zod 4.5 baseline
- Runnable common patterns
1---2name: zod-engineering3description: Select, add, migrate, review, test, or optimize Zod schemas; parse unknown JSON, form, query, environment, event, or tool data; map Zod errors; or use Zod-to-JSON-Schema and backend interoperability. Do not use for static TypeScript-only work, backend-native validation, public API design, authorization, sanitization, or domain-invariant ownership.4---56# Zod Engineering78Use Zod as a runtime boundary adapter, not as a replacement for public-contract,9security, backend, or domain ownership.1011## Use When1213- A JavaScript or TypeScript boundary needs runtime parsing, normalization, or14 typed output.15- A repository selects or migrates Zod, including Zod 3-to-4 compatibility.16- A Zod schema, mapped validation error, generated JSON Schema, or performance17 claim needs review or tests.1819## Boundaries2021Parse `unknown` once at ingress; use parsed output afterwards; map it to plain22application or domain types. Independently validate outbound data that crosses23an untrusted or compatibility-sensitive boundary. Zod parsing is neither24authorization nor sanitization and does not own domain invariants.2526For a cross-language wire boundary use:2728`unknown JSON/IPC → Zod boundary schema → plain wire DTO → backend-native deserializer/validator → backend domain type`2930and reverse it through a stable serialized envelope before a UI/application31parse. See the [patterns and examples](references/patterns-and-examples.md#cross-language-interop).3233## Workflow34351. Inspect `package.json`, the selected lockfile/package manager/runtime,36 TypeScript version, existing validator and import style, framework adapter,37 trust boundary, and installed Zod version.382. Preserve a suitable established validator or Standard Schema adapter unless a39 migration is explicitly requested. Decide whether a public artifact, backend40 contract, or boundary schema owns the contract before writing a schema.413. Decide missing versus `null`, unknown keys, coercion, defaults,42 transforms/codecs, numeric/date/byte representation, size/depth/cardinality43 limits, and error exposure explicitly.444. Parse at the adapter, map inward, normalize errors at the adapter/render45 layer, and test the observable boundary.4647## Core Rules4849- Prefer schema-first contracts with `z.infer`, `z.input`, and `z.output`; use50 `z.toZod<T>()` only for type-first compatibility. Never maintain duplicate51 interfaces and schemas that can drift.52- Compose object shapes or use `.safeExtend()` rather than long `.extend()`53 chains. Use strict discriminated branches for overlapping event or intent54 unions.55- Use object getters for recursive objects; use `z.lazy()` when deferred56 non-object or mutually recursive shapes are clearer.57- Read [patterns and examples](references/patterns-and-examples.md) before58 selecting parse APIs, composition, error mapping, JSON Schema, or interop.5960## Errors And Security6162Map structured issue codes and bounded paths to a stable application/API error63envelope. Do not expose `ZodError`, default library wording, raw input,64transformed values, full issues, or `reportInput` without an explicit sanitized65policy. `z.treeifyError()` and `z.flattenError()` belong only in a presentation66adapter.6768Load [`security-review`](../security-review/SKILL.md) and69[`security-review-evidence`](../security-review-evidence/SKILL.md) for trust70boundaries; they own authorization, sanitization, resource limits, and safe71security evidence. Load [`internationalization-localization`](../internationalization-localization/SKILL.md)72for translated user messages.7374## Testing7576Test accepted and rejected boundaries; optional/null/unknown-key policy;77transforms and input/output divergence; sync and async parsing; mapped error78stability; and generated-contract parity. Add bounded property tests only when79the repository already uses a suitable generator. The runnable oracle is80[common patterns](examples/common-patterns.ts).8182Load [`test-driven-development`](../test-driven-development/SKILL.md) for an83implementation loop and [`testing-strategy`](../testing-strategy/SKILL.md) for84coverage review.8586## Performance And Migration8788Measure representative valid, invalid-with-diagnostics, boolean-validation,89schema construction/reuse, transform, depth, and async workloads before using90`z.compile()` or `z.validate()`. Upstream benchmarks are not local evidence.91Read the dated [Zod 4.5 baseline](references/zod-4.5-baseline.md) before a92version-sensitive change or migration.9394## Routing9596- [`javascript-typescript-engineering`](../javascript-typescript-engineering/SKILL.md)97 owns package/runtime/tooling mechanics.98- [`api-design`](../api-design/SKILL.md) owns public schemas, compatibility,99 versioning, and stable error envelopes.100- [`typescript-javascript-design-patterns`](../typescript-javascript-design-patterns/SKILL.md)101 owns adapter/core placement.102- [`performance-review`](../performance-review/SKILL.md) owns workload evidence.103- [`php-engineering`](../php-engineering/SKILL.md),104 [`python-engineering`](../python-engineering/SKILL.md),105 [`rust-engineering`](../rust-engineering/SKILL.md), and106 [`csharp-dotnet-engineering`](../csharp-dotnet-engineering/SKILL.md) own107 backend-native DTO validation, mapping, serialization, and errors.108109## References110111- [Patterns and examples](references/patterns-and-examples.md)112- [Zod 4.5 baseline](references/zod-4.5-baseline.md)113- [Runnable common patterns](examples/common-patterns.ts)