2026-09-08 · 8 min read
- json-schema
- api-design
- validation
JSON Schema composition combines reusable constraints, but the keywords express validation logic rather than inheritance or object-oriented dispatch. oneOf means exactly one subschema validates, anyOf means at least one validates, and allOf means every subschema validates.
Ambiguous alternatives become production defects when validators, documentation generators, and client generators choose branches differently. A reliable contract makes variants structurally distinguishable and treats annotations separately from validation.
Choose the operator from domain logic
Use oneOf only when overlap is invalid, anyOf when multiple descriptions may legitimately apply, and allOf when independent constraints must all hold. Do not select an operator merely because a generator renders it conveniently.
- Define required properties inside the branch that owns them.
- Place truly shared constraints outside alternatives.
- Use explicit null unions when null is part of the contract.
- State the supported JSON Schema draft and vocabulary.
Create explicit, non-overlapping variants
For tagged unions, require a discriminator property and constrain it with a unique const value in each branch. A discriminator extension can help tooling, but it should not be the only reason the underlying schema selects the correct branch.
Apply additionalProperties or unevaluatedProperties deliberately. Combining closed object schemas with allOf can reject extension fields because property evaluation and closure rules depend on schema structure and draft.
Keep annotations and mutation out of validation assumptions
Defaults, examples, titles, and descriptions are annotations; validation does not necessarily insert or transform values. Format assertion can also vary by vocabulary and validator configuration.
- Document any default-applying preprocessor separately.
- Do not assume validation coerces strings into numbers.
- Pin validator options across development and production.
- Review generated client behavior against server validation.
Build a branch-count test matrix
Use Flashman's JSON formatter for schemas and fixtures, diff to compare variants, case converter for stable discriminator names, regex tester for pattern constraints, and Markdown editor to document branch expectations.
Test fixtures matching zero, exactly one, and several branches; missing and unknown tags; nested composition; closed objects; nulls; formats; referenced schemas; draft upgrades; validator upgrades; and every generated client.