DevFormatLab
← Back to blog

YAML vs JSON vs TOML — When to Use Which

By DevFormatLab Editorial·8 min read
YAMLJSONTOMLConfigurationComparison

The "which serialisation format should I use" argument has been running for as long as configuration files have existed. After fifteen years of watching teams pick the wrong one, the answer is rarely "use the best one" — it is "use the one whose failure modes you can recognise in your sleep". This post is the criteria we use, and a decision tree you can apply in under a minute.

The three contenders, briefly

JSON (RFC 8259) is the data interchange format that escaped. Strict grammar, six value types, one string delimiter (the double quote), one number format (decimal, no leading zeros). It ships a parser in every mainstream language's standard library, and the parser's behaviour is portable across implementations to a degree nobody else has matched.

YAML (1.2 specification) is the configuration format that swallowed the DevOps world. Indentation-based hierarchy, scalars as plain strings, full Unicode support, anchors and aliases for cross-references, three different ways to write a multi-line string. Beautiful when it works; a maintenance burden the moment something goes wrong.

TOML (1.0 specification) is the relatively-newcomer designed specifically for configuration. Key-value structure, dotted-section headers for namespacing, explicit types via syntax (string vs integer vs datetime), no whitespace sensitivity, no anchoring. Designed to be both more human-editable than YAML and more parseable than YAML, and on both counts it usually succeeds.

Eight criteria that matter

  1. Who edits it. If the file is touched by humans with no programming background (operators, designers, marketing), TOML wins on readability — keys and values are explicit, hierarchy is by section header rather than indentation, there is no "did you mean null or ~?" question. If the file is written mostly by tools and rarely touched by humans, JSON wins on tooling — every editor on earth knows how to highlight and validate JSON.
  2. Who reads it. JSON's readers are everywhere. YAML's readers are mostly in Python, Ruby, Go, Rust, Java, Node.js, PHP — the languages that chose YAML as their default configuration format. TOML is everywhere in the Rust ecosystem (Cargo, Just, uv) and is now spreading to Python (pyproject.toml), .NET, Go, and Node.js (package.json has been a TOML-like format for a decade).
  3. Tooling richness. JSON has the most mature tooling (jq, JSON Schema, jsonschema, JSON Patch, JSON Canonicalization Scheme). YAML has decent parsing tools but almost no validation tooling — most YAML deployments end up parsing the file into an internal AST and validating structure with application code, which is fragile. TOML is rising but still younger than JSON.
  4. Strictness. JSON is strict. YAML is loose in dangerous ways: NO, yes, on, and off are all booleans in YAML 1.1 (deprecated in 1.2, but still supported in many parsers for compatibility); trailing commas are allowed in flow style; 2024-01-15 is parsed as a date; 1.0 and 1 parse as the same value or different values depending on the version. TOML is explicit about types via syntax — no guessing.
  5. Size. JSON is the densest of the three on the wire (no quotes around keys, no significant whitespace). YAML is the loosest because of indentation. TOML sits in between, with explicit quoting that adds bytes but removes ambiguity.
  6. Comments. JSON forbids them. YAML and TOML both allow them, with TOML's # style being the more programmer-friendly of the two and YAML's # style being equivalent but visually noisier inside long values.
  7. Round-trip semantics. This is the killer criterion for many real systems. JSON preserve order of insertion (RFC 8259 §4). YAML preserves order but allows duplicates, which downstream consumers hate. TOML does not formally specify key order in maps; most parsers preserve insertion order; some use sorted order.
  8. Embedded-in-source convenience. TOML has the cleanest "first line declares metadata, the rest is the config" ergonomics — Cargo.toml, pyproject.toml, Justfile all use this idiom well. YAML embedded in a shell script (Kubernetes manifests) is bearable if you use the | literal block scalar; otherwise it can wreck your YAML by stripping indentation.

The decision tree

If you are starting from scratch and the answer is not obvious from the criteria above, this short rubric resolves 90% of cases:

  • Default for application-to-application data exchange: JSON. Wire format first, conversion later if a human needs to read it.
  • Default for human-edited configuration files: TOML. Especially if the file lives in version control and gets code-reviewed.
  • Use YAML only if: the file is part of a Kubernetes / GitHub Actions / Ansible ecosystem where YAML is the lingua franca, or the file genuinely needs cross-references (anchors and aliases), or the file is consumed by a tool that already speaks YAML and would be hard to extend.
  • Avoid YAML for: new APIs, new tooling configs where TOML has equivalent expressiveness, anything where round-trip determinism matters, anything that ships to dozens of teams who each need to debug it independently.

Round-tripping is a trap you will hit eventually

A YAML file produced by a tool, edited by a human, and re-parsed by the same tool should ideally be byte-identical to the input. In practice, almost no YAML implementation gets this right — comments shift, key order changes, scalar styles flip between quoted and unquoted, and your diff is full of noise that is not real change. The YAML ↔ JSON converter is the usual escape hatch: round-trip through JSON, get canonical form, decide which side becomes the source of truth.

For signing / hashing / canonicalisation, none of the three formats ships native canonical form (in the sense of RFC 8785 for JSON). JSON has the strongest canonicalisation story; YAML has no canonicalisation spec; TOML has none either. If you need to sign a config, convert to JSON, canonicalise to JCS, sign the canonical bytes — that is the only path that gives you reproducibility across tools.

Putting it together

There is no universal winner. The most reliable heuristic is "use the format whose error message you understand when something goes wrong in production at 2 AM". For most teams in 2026, that is TOML for human-edited configs and JSON for everything that crosses a process boundary. YAML remains the right call when the deployment target is YAML-native (Kubernetes manifests, GitHub Actions, Ansible playbooks, GitLab CI) — but treat each YAML file as a small DSL with its own dialect, and be prepared to defend it.

Related Tools