JSON — the machine-native default
JSON has the strictest, simplest grammar of the three: double-quoted strings, no comments, no trailing commas, no special types beyond string / number / boolean / null / object / array. The result is unambiguous parsing, near-universal language support, and tooling that 'just works'. The downsides for humans are real: writing JSON by hand is painful (quotes, commas), and the lack of comments forces you to use sidecar README files or convention-based '_comment' keys.
YAML — human-friendly until indentation bites
YAML drops most punctuation in favor of indentation and infers types from the value shape ('yes' becomes boolean, '01' becomes octal, '3.14' becomes float). For small configs it reads beautifully. For larger configs it becomes risky — a single mis-indented line can change semantics silently, and accidental string coercions (the famous 'Norway' bug, where the country code 'NO' becomes false) bite production systems. YAML 1.2 mitigates some quirks but most parsers still default to 1.1.
TOML — config-file specialist
TOML (Tom's Obvious Minimal Language) was designed specifically for configuration. It uses sections like INI files, supports comments, has unambiguous types (no 'NO' → false trap), and reads cleanly without indentation rules. Rust's Cargo, Python's pyproject.toml, and Go's modules all use TOML. The trade-off: TOML is less suited to deeply nested structures, and array-of-tables syntax can feel verbose.
Comments
JSON has no comment syntax. YAML uses '#' for line comments. TOML uses '#' for line comments. If your config is heavily commented (annotations, deprecated keys, links to docs), JSON forces you into ugly workarounds. YAML and TOML both win here.
Type system
- JSON: string, number, boolean, null, object, array. No date type, no integer/float distinction.
- YAML 1.2: same plus timestamp, with implicit type inference that can surprise.
- TOML: string, integer, float, boolean, datetime (with timezone), array, table. Explicit and strongly typed.
Error tolerance
JSON parsers usually point you to the exact byte where the parse failed, which is the easiest debugging experience. YAML errors are notoriously vague because indentation problems can cascade. TOML's stricter grammar produces error messages closer to JSON's quality.
Use case recommendations
- Wire format between systems (APIs, microservices): JSON.
- Configuration files for tooling (linters, build tools, package managers): TOML.
- Application configuration with hierarchies (Kubernetes manifests, GitHub Actions, Ansible): YAML — but pin a strict parser and avoid implicit typing.
- Data interchange where humans rarely edit: JSON.
- Small project configs that humans edit often: TOML or carefully-written YAML.