Understanding OpenAPI YAML ↔ JSON
Same spec, two file formats — pick by audience.
Why specs are written in YAML, why tooling consumes JSON, and the diff-friendly formatting habits that survive merges.
Two formats, one model.
OpenAPI doesn't care whether you write the spec as YAML or JSON — both serialise the same in-memory document. Most tools accept either; the conversion is mechanical and lossless. The choice is editorial. Humans write YAML because it has comments, less punctuation, and indentation-driven structure. Build pipelines convert to JSON because every parser ever written handles JSON without ambiguity.
Why YAML for source.
OpenAPI specs are commonly 1,000 to 10,000 lines. JSON at that length is a wall of quotes and braces; YAML is roughly 30 % shorter and reads like an outline. Comments let you explain why a field exists or document a deprecation timeline. Multi-line strings make example payloads readable. The downside: YAML has a fame for confusing edge cases — the "Norway problem" (NO parses as false), version numbers as octals (010 parses as 8), accidental anchors. Use a linter.
Why JSON for delivery.
The published spec — at /openapi.json on a server, or in a documentation site — almost always goes out as JSON. Every parser supports JSON natively; many tools choke on YAML 1.2 features. Swagger UI, Redoc, code generators, and validators all prefer JSON. The publishing step converts the YAML source into JSON; the JSON is the wire format; the YAML is the editor format.
A worked conversion.
YAML 80 lines: paths, components, info — humans skim it in 10 seconds. Converted to JSON: ~140 lines, every key quoted, every list element comma-separated. Same information, ~75 % larger on disk, no comments. Hash one against the other and they're equivalent. The conversion is a one-line script: yaml.parse(text) → JSON.stringify(obj, null, 2).
80 lines YAML → 140 lines JSON
lossless conversion
YAML for editing, JSON for delivery.
yaml.parse + JSON.stringify
= Same model
Diff-friendly habits.
Stable key order — most generators sort alphabetically or by insertion. Indent consistently — 2 spaces is the OpenAPI convention. One field per line; no inline arrays for things that might grow. Comment the why, not the what. Keepinfo.version bumped on every change so the CHANGELOG and the spec agree. These habits keep PR diffs small enough to review.
Tooling that bridges.
swagger-cli bundle resolves $refs and outputs a single-file YAML or JSON. openapi-typescript and friends prefer JSON input. Speccy and Spectral lint either. Stoplight, Postman, ReadMe import both. For CI, lint the YAML source, convert to JSON, run validators, publish. The source-of-truth is one file in one format; every other format is generated.