Skip to content
Back to Blog
format-comparisons

YAML vs JSON vs TOML: Choosing a Config Format

2026-05-17 9 min read

Why Config Format Choice Actually Matters

Pick the wrong configuration format and you will spend hours debugging a missing comma, fighting with a parser that silently drops a key, or explaining to a new team member why the indentation rules are the way they are. These are not hypothetical frustrations — they show up in real codebases every week. YAML, JSON, and TOML are the three formats you will encounter most often in modern software projects. Docker Compose defaults to YAML. Most REST APIs speak JSON. Rust's Cargo package manager chose TOML. Each format made a deliberate trade-off between human writability, machine parseability, and expressive power. Understanding those trade-offs — rather than just copying whatever the tutorial uses — is what separates a maintainable project from a fragile one. This article compares all three formats across the dimensions that matter in practice: syntax strictness, data type support, comment handling, multi-line string ergonomics, and tooling availability. It also covers when you might want to convert between them — and where conversion has real limits you should know about before you start.

JSON: Strict, Portable, and Surprisingly Painful to Write by Hand

JSON (JavaScript Object Notation) was specified by Douglas Crockford in the early 2000s and standardized as RFC 8259 in 2017. Its defining characteristic is strictness. There is exactly one way to represent a given data structure, keys must be double-quoted strings, trailing commas are illegal, and comments do not exist in the spec at all. That strictness is a genuine advantage in machine-to-machine communication. Every major programming language ships a JSON parser in its standard library. The format is unambiguous enough that two independently written parsers will almost always produce identical output from the same input. When a REST API returns a payload or a webpack configuration is read by a build tool, JSON's predictability is exactly what you want. The pain shows up when humans need to author or review JSON directly. Consider a simple database connection block: ```json { "database": { "host": "localhost", "port": 5432, "name": "myapp_production" } } ``` That's readable enough. But add 40 more keys, nest three levels deep, and forget one closing brace — the error message from most parsers will point you to the end of the file rather than the actual problem line. The absence of comments is the other chronic complaint. Teams work around it with keys like `"_comment": "this setting controls cache TTL"`, which is a hack that pollutes the data model. JSON5 and JSONC (JSON with Comments, used by VS Code's settings.json) address some of these issues, but they are non-standard extensions. If you hand a JSONC file to a standard JSON parser, it will fail.

YAML: Maximum Readability, Maximum Footguns

YAML (YAML Ain't Markup Language) was designed explicitly for human readability. It uses indentation rather than braces, supports comments with `#`, handles multi-line strings gracefully, and lets you write bare strings without quotes in most cases. A GitHub Actions workflow or a Kubernetes manifest in YAML is genuinely easier to scan than the equivalent JSON. Here is the same database config in YAML: ```yaml database: host: localhost port: 5432 name: myapp_production # replica set added 2024-03-10 replicas: - replica1.internal - replica2.internal ``` Cleaner, and the comment survives. Multi-line strings are handled with block scalars — the `|` character preserves newlines, `>` folds them — which makes embedding SQL queries or shell scripts in a config file actually workable. The footguns are real, though, and they are well-documented. YAML 1.1 (still used by many parsers including PyYAML's default mode) interprets bare `yes`, `no`, `on`, and `off` as booleans. A country code like `NO` for Norway becomes `false`. The Norwegian flag emoji problem is famous enough that it has its own Wikipedia section. YAML's indentation sensitivity means a single misaligned space silently changes the structure of your document rather than raising an error. The spec itself is 86 pages long — longer than the HTTP/1.1 specification — which is a sign of how much complexity hides beneath the clean surface. For infrastructure-as-code and CI/CD pipelines where humans write and review configs constantly, YAML's readability wins. For configs that are primarily generated by code and rarely touched by hand, that readability advantage shrinks considerably.

TOML: The Pragmatic Middle Ground

TOML (Tom's Obvious, Minimal Language) was created by Tom Preston-Werner, one of GitHub's co-founders, and released in 2013. Version 1.0.0 was finalized in January 2021 after years of careful iteration. Its stated goal is to be a config file format that is obvious to read and unambiguous to parse. TOML borrows INI-style section headers, adds explicit data types, and enforces a flat-ish structure that discourages deeply nested configs: ```toml [database] host = "localhost" port = 5432 name = "myapp_production" # replica set added 2024-03-10 replicas = ["replica1.internal", "replica2.internal"] ``` Unlike YAML, TOML has explicit types for integers, floats, booleans, datetimes (including RFC 3339 timestamps), and arrays. `port = 5432` is always an integer. `enabled = true` is always a boolean. There is no ambiguity about whether a bare value will be coerced into something unexpected. TOML's weakness is representing deeply nested or highly repetitive structures. An array of tables uses `[[section]]` syntax, which works fine for Cargo's `[[bin]]` entries but becomes awkward when you need three or four levels of nesting. At that point YAML's indentation model is actually more natural. TOML also has no merge keys or anchors — features YAML provides for reducing repetition across environments — so large TOML configs tend to repeat themselves. Adoption is growing but still narrower than JSON or YAML. Python's `tomllib` module was added to the standard library in Python 3.11. Before that, you needed a third-party package. If your team works primarily in Rust, Python 3.11+, or Go (with a library), TOML is a comfortable choice. If you are working across many languages or platforms, check parser availability before committing.

Head-to-Head: A Feature Comparison Table

Rather than making you hold all the trade-offs in your head, here is a direct comparison across the features that come up most often in real config work. **Comments:** YAML and TOML both support `#` line comments natively. JSON has no comment syntax in the official spec. **Trailing commas:** YAML and TOML do not use commas as delimiters, so this is not an issue. JSON forbids trailing commas, which is the source of a disproportionate number of parse errors. **Multi-line strings:** YAML has block scalars (`|` and `>`), which are expressive but require understanding the chomping indicators (`-` and `+`). TOML has basic multi-line strings (triple double-quotes) and literal multi-line strings (triple single-quotes). JSON requires explicit `\n` escape sequences — workable but ugly. **Date and time types:** TOML has first-class datetime support with RFC 3339 format. YAML 1.2 has datetime support, though parser behavior varies. JSON has no date type; dates are strings by convention. **Anchors and aliases (DRY configs):** YAML supports `&anchor` and `*alias` for reusing values. TOML and JSON have no equivalent — you repeat yourself or use a preprocessor. **Schema validation:** JSON has JSON Schema, a mature ecosystem with validators in every major language. YAML can use JSON Schema via tools like `ajv` or dedicated validators like `yamale`. TOML schema tooling is less mature. **File size for equivalent data:** JSON is typically the most compact when minified. YAML and TOML are comparable at human-readable indentation levels. For a 500-key config, the difference is rarely more than 10-15%. **Parsing speed:** All three are fast enough for config files that are read at startup. For high-throughput serialization (thousands of parses per second), JSON wins due to the simplicity of its grammar and the optimization effort poured into libraries like `simdjson`.

Converting Between Formats: What Works and What Doesn't

There are legitimate reasons to convert between these formats. You might be migrating a project from one framework to another, generating a YAML config from a JSON API response, or converting a TOML file to JSON so a tool that only understands JSON can consume it. CocoConvert handles JSON-to-YAML, YAML-to-JSON, TOML-to-JSON, and JSON-to-TOML conversions. For straightforward configs — scalar values, nested objects, arrays of strings or numbers — the conversion is lossless and takes a few seconds. Upload your file on the Convert page, select your target format from the dropdown, and download the result. However, it is worth being honest about the cases where conversion is lossy or impossible without manual intervention: **Comments are lost going to JSON.** JSON has no comment syntax. If your YAML or TOML file has inline documentation comments, they will not appear in the JSON output. There is no way around this — it is a fundamental limitation of the JSON spec, not a tool limitation. **YAML anchors are expanded, not preserved.** If your YAML uses `&defaults` and `*defaults` to share values across environments, the converted output will have those values duplicated rather than referenced. The data is correct, but the DRY structure is gone. **TOML datetimes may become strings.** TOML's `created_at = 2024-01-15T09:30:00Z` has no direct equivalent in JSON. CocoConvert outputs it as an ISO 8601 string, which is the standard convention, but the receiving application needs to know to parse it as a date. **Deeply nested TOML to YAML** works fine structurally, but `[[array of tables]]` syntax in TOML maps to YAML sequences of mappings, which can look unfamiliar if you are not expecting it. For anything beyond basic structure, review the converted output before using it in production. A quick `diff` between the original and a round-tripped version will catch most surprises.

Making the Call: Which Format for Which Situation

There is no universally correct answer, but there are clear patterns that hold across most projects. **Use JSON when:** the config is primarily read and written by machines, you need maximum parser compatibility across languages, or you are working with an API or tool that already expects JSON. Package.json, tsconfig.json, and most IDE settings files are JSON for good reason — they are generated and consumed by tooling far more often than they are edited by hand. **Use YAML when:** humans write and review the config regularly, the structure is moderately nested, and you need comments for documentation. Kubernetes manifests, GitHub Actions workflows, Docker Compose files, and Ansible playbooks are all YAML because the people writing them care about readability. If your team is already comfortable with YAML's indentation rules, the footguns are manageable with linting tools like `yamllint` (which you should be running in CI regardless). **Use TOML when:** you want explicit types without YAML's ambiguity, the config is relatively flat, and your language ecosystem has good TOML support. Rust projects with Cargo.toml, Python projects using `pyproject.toml`, and Hugo static site configs are natural fits. TOML is also a good choice for configs that users of your application will edit directly — the syntax is forgiving and the error messages from most parsers are more helpful than YAML's. **When none of the three fits perfectly:** consider whether you actually need a config file at all. Environment variables handle many simple cases. For complex configs with conditional logic, a real programming language (Python, Starlark, Dhall) is sometimes the right answer — though that is a much larger commitment. If you are mid-project and need to switch formats, CocoConvert can handle the mechanical conversion. The judgment call about which format serves your team best is still yours to make.

YAML vs JSON vs TOML: Choosing a Config Format | CocoConvert Blog