Guides · Utiltown
YAML vs JSON: Which Format for Config and CI
"YAML or JSON?" is really three questions: who edits the file, who reads it, and what breaks when a value is misread. YAML optimizes for human editing — comments, anchors, no quote noise — and pays with a coercion layer that has caused real outages. JSON optimizes for machines and interchange and pays in unreadability at scale. Most real-world answers are "both, in different files in the same repo"; the skill is knowing which file gets which, and how to convert without losing something silently.
Where YAML wins
- Human-edited config with review workflows. Kubernetes
manifests, Docker Compose, GitHub Actions, GitLab CI, Ansible. Comments
are load-bearing in review —
# bump with app versionnext to a field survives redeploys; a Slack message does not. - Deep, repetitive structures. Anchors and aliases
(
&base/*base) deduplicate near-identical blocks; block scalars (|,>) keep long strings and scripts readable without\nescapes. - Files humans diff by eye. Indentation shows structure without punctuation; a reviewer sees a changed block, not a changed line of escaped text.
Where JSON wins
- Anything crossing a process boundary. REST and GraphQL payloads, queues, structured logs. Every language parses JSON natively; YAML needs a dependency and a choice among spec versions.
- Machine-generated files. Lockfiles, exports, codegen inputs. No comment support is a feature here — reformatting tools cannot destroy information.
- Predictable typing. Exactly four scalar types. No
parser turns
NOintofalseor010into 8. - Schema tooling. JSON Schema is the industry's most mature validation story, and OpenAPI — however it is serialized — is defined in JSON terms.
The YAML traps that actually break production
- The Norway problem. Under YAML 1.1 (still the default
in PyYAML and several SDKs), unquoted
country: NOparses asfalse. So doon,off,yes, andno— a flag written asenabled: onistruein one library and the string"on"in another. Rule of thumb: when a value reads as text, quote it —country: "NO", notcountry: NO. - Octal and sexagesimal surprises.
mode: 010is the number 8 in YAML 1.1;1:30can parse as 90 (sexagesimal) in some parsers. File permissions and durations are the classic victims. - Version numbers are not strings.
version: 1.10is the number 1.1. Unquoted versions, build numbers, and ZIP codes get silently reinterpreted. - Indentation is semantics. Tabs are illegal; a two-space shift changes nesting. Diff tools that "fix" whitespace can turn one document into another.
- Duplicate keys are undefined behavior. Parsers take
the last, the first, or throw. A merged config with
retries: 3twice can pass review and flip behavior after a library upgrade.
Decision table by use case
| Use case | Pick | Why |
|---|---|---|
| Kubernetes / Compose / Ansible | YAML | Human-edited, comments in review, anchors for repetition; tooling expects it. |
| GitHub Actions / GitLab CI | YAML | Platform-native format; lint it in CI. |
| REST / GraphQL payloads | JSON | Universal parsers, no coercion ambiguity, HTTP ecosystem assumes it. |
| Structured logs / queues | JSON | Machine-written, machine-read; YAML multiline is hostile to log shippers. |
| OpenAPI / JSON Schema | JSON (often YAML-serialized) | Specs are JSON-defined; YAML is an editing convenience. |
| App config read by multiple languages | JSON (or TOML) | One parser behavior everywhere; no 1.1-vs-1.2 roulette across SDKs. |
| npm manifests / lockfiles | JSON | Toolchain contract; byte-stable diffs. |
The conversion workflow that catches surprises
-
Convert YAML → JSON in the
YAML to JSON Converter and read the
output with fresh eyes: every
falsethat used to be a country code, every8that used to be010, shows up here. - Convert the JSON back → YAML and diff against the original. Differences in formatting are fine; a changed value means a coercion bug.
- Quote every flagged scalar in the source and repeat. A stable round-trip is the contract you want before the file ships.
For tabular data that feeds a JSON API, the same discipline applies: the CSV to JSON Converter handles header-row and separator decisions, and the same round-trip check catches encoding and delimiter surprises before they reach the pipeline.
What about TOML, HCL, and XML?
They occupy real niches: TOML (Cargo, pyproject) is deliberately unambiguous and excellent for small human-edited configs; XML survives in SOAP ecosystems and document formats; HCL is Terraform's domain. The same review question applies to each of them: if a generated file can drift from its source, the conversion — not the format — is what needs a test.
Frequently Asked Questions
Can YAML do everything JSON can?
Structurally yes — JSON is a subset of YAML 1.2 with only minor edge cases (duplicate keys being the famous one), so in practice any JSON document parses as YAML. The reverse is not true: comments, anchors and aliases, multi-document streams, unquoted scalars, and block scalars have no JSON representation. Converting YAML to JSON keeps the data but discards comments and formatting, so a generated JSON file is never a drop-in replacement for a hand-maintained YAML config.
What is the Norway problem?
YAML 1.1 parsers interpret the unquoted token NO as the boolean false, so country: NO becomes false in parsed output. The same rule turns on, off, yes, and no into booleans. The fix is to quote every value that is semantically a string: country: "NO". The Utiltown YAML↔JSON converter surfaces this instantly — convert, inspect the result, then fix the source with explicit quotes.
Is YAML or JSON better for CI configuration?
Both dominate in practice: GitHub Actions and GitLab CI are YAML, many build tools accept JSON. Choose YAML when humans edit the file often and comments matter; choose JSON when files are machine-generated or strictly schema-validated. What matters more is validating the config in CI so a bad indent or coerced scalar fails the build, not production.
Why do Kubernetes and Docker Compose use YAML?
History and ergonomics. Manifests are written and reviewed by humans, are often long, and benefit from comments and anchors. Kubernetes accepts JSON too (its API is JSON), but hand-writing hundreds of quoted keys and braces is worse for review — especially when a comment needs to sit next to a specific field.
How do I convert between YAML and JSON safely?
Convert in a tool that runs locally and shows both directions, then round-trip: YAML → JSON → YAML and compare with the original. Formatting may differ; values must not. Any value difference is a type-coercion surprise (booleans, octals, versions) that needs explicit quoting in the source. The Utiltown YAML to JSON Converter does both directions in the browser.