The JSON vs YAML question comes up every time a team creates a new configuration file. Both formats can represent the same data. Both have libraries in every language. Both are text-based and version-control friendly. The choice isn't about capability — it's about who's reading and writing the file.
This guide compares JSON and YAML specifically for configuration files — not APIs, not data interchange, not database storage. Configuration files have unique requirements: humans edit them, they need documentation (comments), they're often modified under pressure (debugging at 2 AM), and errors in them can bring down production systems.
Comments: YAML's Biggest Advantage
This is the single most impactful difference. YAML supports comments (# like this). JSON does not.
In a configuration file, comments serve critical purposes:
- Explaining why a value is set, not just what it is:
# Reduced from 100 to 25 after the 2026-02 OOM incident - Temporarily disabling settings while debugging:
# cache_ttl: 3600 - Documenting valid values:
# Options: debug, info, warn, error - Linking to tickets or documentation:
# See JIRA-1234 for context
Without comments, this context lives in commit messages (which nobody reads while debugging), external documentation (which drifts out of sync), or nowhere at all. The inability to comment out a line during debugging is a daily friction point for JSON config files.
Workarounds for JSON: JSONC (JSON with Comments, used by VS Code) allows // and /* */ comments. JSON5 adds comments plus other human-friendly features. But both are non-standard — most JSON parsers reject them, and using them means your config can't be processed by standard JSON tools.
Readability: YAML Wins for Humans
Compare the same config in both formats:
// JSON
{
"server": {
"host": "0.0.0.0",
"port": 8080,
"ssl": {
"enabled": true,
"cert": "/etc/ssl/cert.pem",
"key": "/etc/ssl/key.pem"
}
},
"database": {
"url": "postgres://localhost:5432/mydb",
"pool_size": 25
},
"features": ["auth", "logging", "metrics"]
}# YAML
server:
host: 0.0.0.0
port: 8080
ssl:
enabled: true
cert: /etc/ssl/cert.pem
key: /etc/ssl/key.pem
database:
url: postgres://localhost:5432/mydb
pool_size: 25
features:
- auth
- logging
- metricsYAML is visually cleaner: no braces, no brackets, no quoted keys, no commas. The indentation communicates structure directly. For humans scanning a config file to find and change a value, YAML's lower syntactic noise is a real productivity advantage.
The counter-argument: JSON's braces make structure explicit. In YAML, a one-space indentation error creates a structurally different document that's valid YAML but wrong. In JSON, a missing brace is a syntax error that the parser catches immediately. Explicitness vs. brevity — JSON fails loudly, YAML fails silently.
Footguns: YAML Has Many, JSON Has Few
JSON's strict syntax means there are very few ways to create valid-but-wrong JSON. If you forget a comma, miss a quote, or add a trailing comma, the parser throws an error. JSON's rigidity is its safety net.
YAML's flexibility creates multiple categories of silent bugs:
| Footgun | Example | Result |
|---|---|---|
| Boolean coercion | country: NO | Parsed as false (YAML 1.1) |
| Number coercion | version: 3.10 | Parsed as float 3.1 |
| Octal numbers | port: 0777 | Parsed as 511 (YAML 1.1) |
| Indentation error | 2 spaces vs 3 spaces | Wrong nesting, valid YAML |
| Tab characters | Tab instead of spaces | Parse error (hard to see) |
| Trailing whitespace | key: (space after colon) | Null value instead of error |
| Unquoted strings | value: null | Null, not the string "null" |
JSON's footgun list is much shorter: number precision loss on large integers, no way to represent Infinity or NaN, and string-only keys. That's about it. If you want a format where valid == correct, JSON is safer.
Type Coercion vs. Strict Typing
JSON's types are explicit. "8080" is always a string. 8080 is always a number. true is always a boolean. There's no ambiguity and no coercion.
YAML infers types from values. 8080 is a number. true is a boolean. But yes is also a boolean in YAML 1.1, and NO is false, and 3.10 is a float, and 0777 is octal. This inference saves keystrokes when it guesses right and creates bugs when it guesses wrong.
YAML 1.2 significantly reduced the problem by restricting booleans to only true/false and removing sexagesimal numbers. But YAML 1.2 adoption is incomplete — PyYAML still defaults to 1.1. The safe practice in YAML: quote any value that isn't obviously a number, boolean, or null.
For configuration files specifically, type coercion bugs are dangerous because configs are often modified rarely and tested even less. A YAML type coercion bug might sit unnoticed for months until someone changes a seemingly unrelated value and triggers a different code path. JSON's strict typing prevents this entire class of bug.
Tooling and Editor Support
Both formats have excellent tooling, but with different strengths:
| Tool Category | JSON | YAML |
|---|---|---|
| Parsers (language support) | Standard library in every language | Libraries available for every language (some with 1.1/1.2 differences) |
| Schema validation | JSON Schema (mature, widespread) | Uses JSON Schema via YAML-to-JSON conversion |
| Editor support | Universal syntax highlighting and validation | Good but indentation issues can be subtle |
| Linting | Most editors validate JSON on save | yamllint (separate tool), editor extensions |
| Formatting | jq, python -m json.tool, Prettier | yq, Prettier (with YAML plugin) |
| Command-line processing | jq (powerful, widely installed) | yq (multiple implementations, less standardized) |
| Diff readability | Good (structural changes visible) | Excellent (minimal syntax noise in diffs) |
JSON has an edge in tooling maturity, especially jq for command-line processing. YAML diffs are cleaner because of less syntactic noise, which matters for code review of config changes.
YAML's Merge Key: Powerful but Risky
YAML supports anchors (&name) and aliases (*name) to avoid repetition, plus a merge key (<<) to merge mappings:
defaults: &defaults
timeout: 30
retries: 3
production:
<<: *defaults
timeout: 60
staging:
<<: *defaultsThis is genuinely useful for configs with shared defaults. JSON has no equivalent — you must duplicate values or handle inheritance in application code.
The risk: the merge key (<<) is a YAML 1.1 extension, not part of the YAML 1.2 specification. Not all parsers support it. When they do, the merge order (which key "wins" when there are conflicts) can vary between implementations. Test merge key behavior in your specific parser before relying on it in production configs.
Recommendations by Use Case
| Use Case | Recommendation | Why |
|---|---|---|
| Application config (humans edit) | YAML (or TOML) | Comments, readability, multiline strings |
| Application config (machine-generated) | JSON | Strict parsing, universal support, no coercion bugs |
| CI/CD pipelines | YAML | Required by GitHub Actions, GitLab CI, etc. |
| Kubernetes manifests | YAML | Required. Deep nesting makes JSON impractical. |
| API specifications | Either (both supported by OpenAPI) | YAML for authoring, JSON for machine consumption |
| VS Code settings | JSONC | VS Code chose JSONC (JSON with Comments) |
| TypeScript config | JSON | tsconfig.json. Ecosystem convention. |
| Node.js package config | JSON | package.json. Ecosystem convention. |
| Python project config | TOML | pyproject.toml. Ecosystem convention. |
| Rust project config | TOML | Cargo.toml. Ecosystem convention. |
The honest answer for new projects: if you can choose freely, use TOML. It has comments (unlike JSON), explicit types (unlike YAML), and no indentation footguns. TOML only falls short for deeply nested configs, which is when YAML's indentation-based nesting becomes necessary.
The JSON vs YAML debate is less about which format is better and more about which tradeoffs you prefer. JSON trades human-friendliness for machine-safety. YAML trades precision for expressiveness. Neither is wrong — they're optimized for different consumers.
If you're writing config that machines generate and read, use JSON. If you're writing config that humans maintain and debug, use YAML (but quote your strings). If you have a clean slate and your config isn't deeply nested, skip both and use TOML — it's what both formats wish they were for configuration.