DevKit
← Back to Blog

YAML vs JSON for Config: Which One Should You Use?

7 min read

Pick almost any modern tool and you will hit a configuration file. Kubernetes manifests, GitHub Actions workflows, Docker Compose, ESLint settings — some use YAML, some use JSON, and a few accept both. Choosing between them is less about taste than about who reads the file and how often a human edits it by hand.

Here is a grounded comparison of the two formats, the traps that bite people, and a simple rule for deciding.

The same data in both formats

Start with a small server config so the shapes are side by side. First JSON:

{
  "server": {
    "host": "localhost",
    "port": 8080,
    "tls": false
  },
  "features": ["search", "export"]
}

Now the equivalent YAML:

server:
  host: localhost
  port: 8080
  tls: false
features:
  - search
  - export

Same structure, fewer symbols. YAML drops the braces, quotes, and most commas. That reduction is the entire pitch for YAML — and also the root of most of its surprises.

Comments

This is the biggest everyday difference. JSON has no comments. The spec simply does not include them, so you cannot leave a note next to a tricky setting without a parser that supports a nonstandard extension like JSONC.

YAML supports comments with a hash:

port: 8080  # fallback port, overridden by PORT env var

For a file a human maintains, comments matter a lot. Being able to explain why a value is set the way it is often saves the next person an hour of guessing.

Whitespace sensitivity

JSON does not care about indentation. You can minify it to one line and it parses the same. YAML cares deeply. Indentation defines structure, and it must be spaces — tabs are not allowed. Mixing two spaces here and four spaces there, or sneaking in a tab, produces errors that are hard to spot because the file still looks fine to your eye.

A classic mistake is a misaligned list item:

features:
  - search
   - export   # extra space, now this breaks

In large YAML files, indentation bugs are the number one cause of frustration. JSON trades verbosity for never having to think about this.

JSON is a subset of YAML

A useful fact: YAML 1.2 is a strict superset of JSON. Any valid JSON document is also valid YAML, which means a YAML parser can read your JSON files directly. This is why many tools accept both without extra code. It also gives you an escape hatch — if a YAML block gets confusing, you can drop into inline JSON-style syntax inside the same file:

features: ["search", "export"]
server: { host: localhost, port: 8080 }

The Norway problem

Here is the trap that catches everyone eventually. YAML tries to be helpful by guessing types. Unquoted words that look like booleans get converted. In YAML 1.1, which many parsers still follow, these all become true or false:

enabled: yes
debug: no
active: on
locked: off

The famous case is country codes. The code for Norway is NO, so this:

countries:
  - NO
  - SE
  - FI

parses the first entry as the boolean false instead of the string NO. The fix is to quote anything you want kept as text:

countries:
  - "NO"
  - "SE"
  - "FI"

Numbers have a related issue. A version string like 1.20 may be read as the number 1.2, dropping the trailing zero. When in doubt, quote. JSON never does this guessing — a string is a string because it has quotes, full stop.

Anchors and reuse

YAML has a feature JSON lacks: anchors and aliases, which let you define a block once and reference it elsewhere. An anchor is marked with an ampersand, and an alias pulls it back in with an asterisk:

defaults: &defaults
  timeout: 30
  retries: 3

staging:
  <<: *defaults
  host: staging.example.com

production:
  <<: *defaults
  host: example.com

The merge key pulls the shared defaults into each environment. This keeps repeated config DRY, but it also makes files harder to read for newcomers and some parsers disable it for safety. Use it sparingly.

When to pick each

The deciding question is who touches the file. Choose JSON when a machine produces or consumes it: API request and response bodies, data interchange between services, build artifacts, anything programmatic. Its rigidity is a feature there, and nearly every language parses it natively with no dependency.

Choose YAML when a human edits it regularly: application config, CI pipeline definitions, infrastructure manifests. Comments and the lighter syntax pay off, and the type-guessing traps are manageable once you know to quote ambiguous values.

A reasonable default: data on the wire goes in JSON, config a person maintains goes in YAML. When you need to move between them while debugging, convert rather than retype.