YAML and JSON are both data serialisation formats — they represent the same kinds of data structures. But they were designed with different priorities. JSON prioritises machine readability and unambiguous parsing. YAML prioritises human readability and writability. That fundamental difference explains almost every divergence between them.
Convert between them instantly with the YAML to JSON and JSON to YAML converters — free, browser-based, nothing stored.
The Same Data, Two Formats
// JSON
{
"server": {
"host": "localhost",
"port": 8080,
"debug": true
},
"database": {
"url": "postgres://localhost/mydb",
"pool_size": 5
},
"allowed_hosts": ["localhost", "staging.example.com"]
}
# YAML
server:
host: localhost
port: 8080
debug: true
database:
url: postgres://localhost/mydb
pool_size: 5
allowed_hosts:
- localhost
- staging.example.com
The YAML is 30% fewer characters and far easier to read and edit by hand. The JSON is unambiguous, tooling-friendly, and doesn't rely on indentation to convey structure.
Key Differences
| Feature | JSON | YAML |
|---|---|---|
| Comments | ❌ Not supported | ✅ # comment |
| Quotes for strings | Always required | Usually optional |
| Multiline strings | Escape with \n | Native block literals |
| Structure | Braces and brackets | Indentation |
| Data types | String, number, bool, null, array, object | All of JSON + dates, anchors, aliases |
| Spec ambiguity | Minimal | Significant (Norway Problem, etc.) |
| Parse complexity | Very low | High |
| API usage | Universal | Not used in APIs |
YAML's Superpower: Comments
This alone makes YAML indispensable for configuration files that humans maintain. JSON forces workarounds like "_comment" keys or separate documentation. YAML allows inline explanation of what each value does and why:
server:
# Use 0.0.0.0 in production to bind all interfaces
host: localhost
# Must match the port in your reverse proxy config
port: 8080
# Never enable in production — exposes internal details
debug: false
YAML's Notorious Gotchas
The Norway Problem
Unquoted values in YAML are parsed as their most specific type. The country code NO is interpreted as the boolean false. This trips up config files with country codes, version numbers, and similar strings:
# These are all parsed as booleans, not strings!
country: NO # → false
enabled: yes # → true
flag: on # → true
status: off # → false
# Fix: always quote strings that could be misinterpreted
country: "NO"
enabled: "yes"
Indentation Is Structure
Unlike JSON's explicit braces, YAML's structure is entirely defined by indentation. One wrong space breaks the parse. Use a linter or validator in your editor.
Multiline String Modes
YAML has two multiline string modes that behave differently with trailing newlines — a source of subtle bugs:
literal_block: |
Line 1
Line 2
# Preserves newlines, includes trailing newline
folded_block: >
Line 1
Line 2
# Folds newlines into spaces (one paragraph)
When to Use Each
| Use Case | Best Format |
|---|---|
| Config files humans edit | YAML — comments, readability |
| CI/CD pipelines (GitHub Actions, GitLab CI) | YAML — industry standard |
| Kubernetes manifests | YAML — ecosystem default |
| Docker Compose | YAML — format requirement |
| REST API responses | JSON — universal parsing |
| Package manifests (package.json, etc.) | JSON — tooling expects it |
| Data exchange between systems | JSON — unambiguous, safe |
| OpenAPI / Swagger specs | Either (YAML is more readable) |