JSON vs YAML vs TOML: Choosing a Config Format
An honest comparison of JSON, YAML and TOML for configuration — readability, comments, tooling, and the specific ways YAML surprises people.
Table of contents
- The same config in each
- JSON: use it for machines
- YAML: use it for humans, carefully
- TOML: use it when the structure is shallow
- A decision rule
- Converting between them
- Frequently asked questions
- Is YAML a superset of JSON?
- Why do my YAML comments disappear after formatting?
- Which is fastest to parse?
- Should I use environment variables instead?
- Related reading
- References
The three formats optimise for different things. Picking well is mostly about who edits the file.
The same config in each#
{
"name": "tweakpad",
"port": 3000,
"features": { "analytics": false },
"hosts": ["a.example.com", "b.example.com"]
}name: tweakpad
port: 3000
features:
analytics: false
hosts:
- a.example.com
- b.example.comname = "tweakpad"
port = 3000
hosts = ["a.example.com", "b.example.com"]
[features]
analytics = falseJSON: use it for machines#
Strengths: one unambiguous specification, a parser in every language, native in JavaScript, trivially machine-generated.
Weaknesses: no comments, no multi-line strings, punctuation-heavy, and a trailing comma breaks the file.
Use it for API payloads, package.json-style manifests, and anything primarily written by a program. The lack of comments is the single biggest strike against it for hand-edited config — which is exactly why tsconfig.json is really JSONC.
YAML: use it for humans, carefully#
Strengths: the least punctuation, comments, multi-line strings, anchors for reuse. It is why Kubernetes, GitHub Actions and Docker Compose all use it.
Weaknesses: significant whitespace, a large and subtle specification, and a set of type-coercion behaviours that genuinely cause outages.
The famous one:
countries:
- GB
- NO # ← YAML 1.1 parses this as the boolean falseNO, ON, OFF, Y, N and TRUE were all boolean-ish in YAML 1.1. YAML 1.2 fixed this, but plenty of deployed parsers still follow 1.1. Quote any value that could be read as a boolean.
Two more worth knowing:
version: 1.10 # a float → 1.1, losing the zero
sha: 0e1234 # may parse as a number in scientific notation
time: 12:30 # sexagesimal in YAML 1.1 → 750The general defence is the same each time: quote strings that look like something else.
TOML: use it when the structure is shallow#
Strengths: unambiguous, comments, no significant whitespace, first-class dates, obvious semantics. Rust's Cargo.toml and Python's pyproject.toml made it mainstream.
Weaknesses: deep nesting is genuinely awkward. Compare four levels:
[servers.production.database.replica]
host = "db-1"That is legal but hard to scan, and arrays of nested tables ([[a.b.c]]) get worse. TOML is excellent for flat-to-two-levels config and a poor fit for deeply hierarchical data.
A decision rule#
- Machine-to-machine, or generated code writes it → JSON
- Humans edit it and the structure is deep → YAML, with strings quoted
- Humans edit it and the structure is shallow → TOML
- You need comments and JSON tooling → JSON5 or JSONC, declared explicitly
Converting between them#
All three map onto the same underlying data model — maps, arrays, strings, numbers, booleans — so conversion is usually lossless in one direction only. Going to JSON loses comments (JSON has nowhere to put them) and YAML anchors (they get expanded). That is worth knowing before you run a formatter over a heavily-commented manifest.
Frequently asked questions#
Is YAML a superset of JSON?#
YAML 1.2 is, so any valid JSON file is valid YAML. The reverse is not true.
Why do my YAML comments disappear after formatting?#
Because formatting parses the file into a data structure and re-emits it, and YAML's data model has no representation for comments. Keep the original.
Which is fastest to parse?#
JSON, by a wide margin — the grammar is tiny and parsers are heavily optimised. It only matters at high volume.
Should I use environment variables instead?#
For secrets and per-deployment values, yes. Config files are for structure; env vars are for values that differ between environments. Mixing the two roles is how secrets end up in git.
Related reading#
- JSON Formatting Best Practices
- Convert or validate with the YAML Formatter and JSON Formatter.