Three formats, three philosophies
JSON is a data interchange format: minimal syntax, universal parsing, no comments, no trailing commas. YAML is a configuration language optimized for humans: whitespace-significant, expressive, and — because of that — full of edge cases. TOML is configuration with a grammar: explicit, boring, hard to get wrong.
Feature comparison
| JSON | YAML | TOML | |
|---|---|---|---|
| Comments | ✗ | ✓ | ✓ |
| Multi-line strings | Escaped only | ✓ (block scalars) | ✓ |
| Dates/times | string | ✓ (unquoted!) | ✓ first-class |
| Anchors/refs | ✗ | ✓ | ✗ |
| Trailing commas | ✗ | n/a | tables: n/a |
| The Norway problem | n/a | ✗ no → false | ✓ strings stay strings |
Choose by job
- APIs, anything machine-to-machine: JSON. Every language parses it natively; schema tooling (JSON Schema, OpenAPI) is built on it.
- Kubernetes, CI pipelines, docker-compose: YAML — ecosystem standard, like it or not. Use a linter (yamllint) and quote all strings that could parse as bool/number (
"no","0755",version: "1.2"). - App config you control (Rust, Python, Hugo): TOML — comments, obvious typing, no indentation traps.
YAML traps, concretely
country: noparses ascountry: false(also:yes,on,off). Quote strings.version: 1.10is the float 1.1 — quote semver, always.- Tabs are illegal for indentation; copy-paste from chat apps injects non-breaking spaces that break parsers invisibly.
- Anchors (
&ref/*ref) are powerful but make files un-greppable; use sparingly in shared repos.
Interop
All three convert losslessly except for the extras: comments drop in JSON, YAML dates become strings, TOML tables reorder. When configs flow between systems, convert to JSON as the canonical middle step. Try it with your own files in the JSON ↔ YAML and TOML → JSON converters — all client-side.