JSON vs YAML — what actually differs, and when each wins

YAML can express everything JSON can, more readably, but trades that readability for ambiguity: nesting by indentation, implicit typing, and features that surprise even careful people.

Published

Every config format argument eventually comes down to JSON vs YAML, and the honest answer is that they are good at different jobs. Here is what actually differs — not the marketing version, the spec-level version.

The grammar difference that shapes everything

JSON is structure made of explicit tokens: braces, brackets, quotes. Nesting is stated.

{
  "name": "deploy",
  "steps": ["build", "test"],
  "cache": { "paths": ["dist/"] }
}

YAML is structure made of layout: indentation is the nesting. Quotes are optional, commas are optional, and scalars are typed by how they look.

name: deploy
steps:
  - build
  - test
cache:
  paths:
    - dist/

JSON’s grammar fits on a postcard and nothing in it is ambiguous. YAML’s is a language with anchors, aliases, multi-document streams, and eleven ways to write a string. Readability is the payoff; surprise is the price.

The YAML behaviors that bite people

These are not folklore; each one is specified behavior:

  • Implicit typing. no parses as false. yes as true. 1.2.3 as a string, but 1e3 as a number. The Norway problem — a country code NO becoming boolean false — is the famous one. Quote anything that must stay a string.
  • Indentation errors are semantic. In JSON a misplaced brace is a syntax error. In YAML a two-space shift can silently re-parent a mapping, producing valid YAML with different structure than you meant.
  • The colon-in-string trap. url: http://example.com is fine, but {url: "a: b"} inside flow style can split on the colon. Flow style, the {"a": 1} form, is legal YAML but reintroduces every JSON rule inside it — use one style or the other.
  • Anchors and aliases (&anchor / *alias) give you references and DRY configs, and give whoever edits the file next a puzzle. In JSON there is no such feature — and therefore no such puzzle.
  • Duplicate keys. Most YAML parsers take the last one and say nothing, same as JSON tools that should know better.

Where each one wins

JSON wins when a machine writes it and machines read it. API bodies, data at rest, anything logged or streamed, anything version-controlled as data. It parses everywhere, is subset-strict, and every serious language has a fast parser. It also round-trips: what you write is what was meant.

YAML wins when a human writes it and mostly humans read it. CI pipelines, compose files, cluster configs. Indented lists of build steps genuinely read better than their JSON equivalents, comments are possible (JSON cannot have them, by design), and anchors can de-duplicate genuinely repetitive config.

JSON wins for numbers, quietly. YAML 1.2 fixed the worst of it, but integer-vs-float behavior and large-number handling still vary by parser; JSON’s decimal-only numbers are boring in the good sense. And if your ids exceed JavaScript’s safe range, a JSON tool that preserves them exactly matters more than the format choice.

Converting between them

The mapping is lossless in the JSON→YAML direction and mostly lossless coming back — with two exceptions. YAML timestamps become strings unless you choose otherwise, and YAML’s implicit types mean "true" in your file was already boolean by the time any converter saw it. The converters here (JSON to YAML, YAML to JSON) quote correctly on the way out and keep numbers exact on the way back, and say so when something cannot round-trip.