YAML vs JSON — Key Differences Explained with Examples
YAML and JSON describe the same kinds of data — objects, arrays, strings, numbers, booleans — yet they feel completely different to work with. JSON is strict, noisy with punctuation, and beloved by machines. YAML is clean, comment-friendly, and beloved by humans, right up until an unquoted value silently turns into a boolean. This guide puts the two formats side by side: syntax, comments, implicit typing surprises like the famous Norway problem, anchors, the superset relationship, and clear rules for when each one wins.
The Same Data, Side by Side
Here is an identical application config expressed in both formats. Notice how YAML drops the braces, brackets, quotes, and commas, using indentation for structure instead:
# ---------- JSON ----------
{
"name": "payment-service",
"replicas": 3,
"debug": false,
"regions": ["ap-south-1", "eu-west-1"],
"database": {
"host": "db.internal",
"port": 5432
}
}
# ---------- YAML (same data) ----------
name: payment-service
replicas: 3
debug: false
regions:
- ap-south-1
- eu-west-1
database:
host: db.internal
port: 5432For a 10-line config the difference is cosmetic. For a 400-line Kubernetes manifest, YAML's reduced punctuation is the difference between a file you can review and one you cannot.
Comments: YAML Has Them, JSON Does Not
This is the single most practical difference. JSON has no comment syntax — by deliberate design, to keep the grammar minimal. YAML supports # comments anywhere:
replicas: 3 # bumped from 2 after Diwali traffic spike timeout: 30 # seconds — keep under the ALB idle timeout (60) # Temporarily disabled until the vendor fixes rate limiting: # webhook_url: https://hooks.example.com/notify
Teams using JSON for config resort to hacks like "_comment" keys or JSONC (JSON with comments, used by VS Code) — both are workarounds for a feature YAML has natively. If a human needs to explain why a value is set, YAML wins.
Implicit Typing: The Norway Problem
JSON requires quotes around every string, so there is never doubt about a value's type. YAML guesses types from unquoted content — convenient, but full of traps. The most famous is the Norway problem: in YAML 1.1, no, yes, on, and off are parsed as booleans, so a list of country codes breaks on Norway:
countries: - IN # → "IN" (string, fine) - DE # → "DE" (string, fine) - NO # → false (boolean! Norway just disappeared) version: 1.10 # → 1.1 (float — trailing zero lost) zip: 01234 # → 668 (octal number in YAML 1.1!) time: 12:30 # → 750 (sexagesimal — base 60 — in YAML 1.1) # The fix — quote anything ambiguous: countries: ["IN", "DE", "NO"] version: "1.10" zip: "01234"
YAML 1.2 removed most of these (only true/false are booleans), but many parsers — including Python's PyYAML default mode — still follow 1.1 rules. The safe habit: quote any string that could look like something else. JSON never has this class of bug.
Anchors and References: YAML's DRY Feature
YAML can define a block once and reuse it — something JSON simply cannot express. An anchor (&name) labels a node, an alias (*name) reuses it, and merge keys (<<:) inline it into another mapping:
defaults: &defaults retries: 3 timeout: 30 region: ap-south-1 staging: <<: *defaults # inherits retries, timeout, region replicas: 1 production: <<: *defaults replicas: 5 timeout: 60 # override just this key
This is heavily used in docker-compose files and GitLab CI to avoid repeating service definitions. The caveat: converting such YAML to JSON flattens the anchors — every alias is expanded into a full copy, and the deduplication is lost.
The Superset Relationship
YAML 1.2 was explicitly designed as a superset of JSON: any valid JSON document is also valid YAML. Braces and brackets are YAML's "flow style", so {"a": [1, 2]} parses identically in both. Practical consequences:
- You can paste JSON into any YAML file (a Kubernetes manifest, a GitHub Actions workflow) and it just works.
- A YAML parser doubles as a lenient JSON parser — handy in tooling.
- The reverse fails: comments, anchors, multi-line block strings, and multiple documents (
---) cannot survive a trip to JSON. - Every YAML document can be converted to JSON (losing comments and anchors), which is exactly what a YAML to JSON converter does.
When Each Format Wins
- YAML wins for human-edited config — Kubernetes, docker-compose, GitHub Actions, Ansible, CI pipelines. Comments and clean syntax matter most here.
- JSON wins for machine-to-machine data — REST APIs, lock files (package-lock.json), serialized state. Strict grammar, universal parser support, and no typing surprises.
- JSON wins for security-sensitive parsing — its tiny grammar has far less attack surface than a full YAML parser.
- YAML wins when files get long — anchors kill duplication, and no trailing-comma errors ever.
- JSON wins inside JavaScript —
JSON.parseis built in; YAML needs a dependency.
Many stacks use both: YAML at the human-editing edge, converted to JSON the moment a machine takes over. If you regularly move data between the two, keep a converter in your toolbox.
Frequently Asked Questions
Yes, practically. YAML 1.2 was designed so that every valid JSON document is also valid YAML — you can paste JSON into a YAML parser and it will load. The reverse is not true: YAML features like comments, anchors, and multi-line strings have no JSON equivalent.
In YAML 1.1, unquoted values like no, yes, on, and off are implicitly converted to booleans. So a country code list containing NO (Norway) silently becomes false. The fix is to quote ambiguous strings ("NO") or use a YAML 1.2 parser, which only treats true and false as booleans.
Use YAML when humans write and maintain the file — it supports comments and is easier to read, which is why Kubernetes, GitHub Actions, and docker-compose use it. Use JSON when machines generate or exchange the data — APIs, lock files, and serialization — because its strict grammar removes parsing ambiguity.