Docker uses YAML. Rust uses TOML. REST APIs use JSON. Akka uses HOCON. Each format solves different problems.
Here’s the same configuration in all four formats to see the differences:
JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"server": {
"host": "0.0.0.0",
"port": 8080,
"workers": 4,
"timeout": 30
},
"database": {
"host": "localhost",
"port": 5432,
"name": "myapp",
"pool_size": 20,
"ssl": true
},
"features": {
"caching": true,
"logging": true,
"monitoring": false
}
}
YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server:
host: 0.0.0.0
port: 8080
workers: 4
timeout: 30
database:
host: localhost
port: 5432
name: myapp
pool_size: 20
ssl: true
features:
caching: true
logging: true
monitoring: false
TOML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[server]
host = "0.0.0.0"
port = 8080
workers = 4
timeout = 30
[database]
host = "localhost"
port = 5432
name = "myapp"
pool_size = 20
ssl = true
[features]
caching = true
logging = true
monitoring = false
HOCON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Server configuration
server {
host = "0.0.0.0"
port = 8080
workers = 4
timeout = 30
}
// Database settings
database {
host = localhost
port = 5432
name = myapp
pool_size = 20
ssl = true
}
// Feature flags
features {
caching = true
logging = true
monitoring = false
}
Now let’s break down each format.
JSON: Universal Data Format
Best for: APIs, data exchange, browser communication
Pros:
- Universal support across all languages
- Fast parsing speed
- Strict syntax reduces errors
- Perfect for machine-to-machine communication
Cons:
- No comment support
- Verbose with quotes and braces
- Trailing commas break parsing
- Multi-line strings need escaping
Real-world usage:
- REST APIs and web services
- Node.js package.json
- MongoDB documents
- Browser storage (localStorage)
When to use: Data exchange between systems, APIs, any machine-generated configs.
YAML: Human-Readable Format
Best for: Docker, Kubernetes, CI/CD pipelines
Pros:
- Highly readable with minimal syntax
- Supports comments (
#) - Excellent for complex nested structures
- Multi-line strings without escaping
- Industry standard for DevOps
Cons:
- Indentation-sensitive (spaces matter)
- Slower parsing than JSON
- Different parsers may behave differently
- Security risks if not parsed safely
Real-world usage:
- Docker Compose files
- Kubernetes manifests
- GitHub Actions workflows
- Ansible playbooks
- OpenAPI specifications
When to use: Container orchestration, CI/CD pipelines, complex configurations with deep nesting.
TOML: Configuration Specialist
Best for: Application configs, build tools, package manifests
Pros:
- Clean, minimal syntax
- Supports comments (
#) - Explicit data types (string, integer, float, date)
- Unambiguous parsing
- Native date/time support
- Better error messages than JSON
Cons:
- Smaller ecosystem than JSON/YAML
- Complex nesting can get verbose
- Less familiar to most developers
Real-world usage:
- Rust Cargo.toml
- Python pyproject.toml
- Application configuration files
- Build tool settings
When to use: Human-edited configs that need comments, Rust/Python projects, settings that change frequently.
HOCON: Flexible JSON Superset
Best for: JVM applications, complex configs, microservices
Pros:
- Superset of JSON (all JSON is valid HOCON)
- Supports comments (
#or//) - Unquoted keys for cleaner syntax
- Variable substitutions and includes
- Can merge configurations
- Path expressions for navigation
Cons:
- Mainly JVM ecosystem (Scala, Java)
- Less adoption outside JVM world
- More complex than simple formats
- Requires specific parsers
Real-world usage:
- Akka framework
- Play Framework
- Lightbend/Typesafe projects
- Scala applications
- JVM microservices
When to use: JVM applications, complex configs needing modularity, projects requiring config inheritance and substitutions.
Technical Comparison
Feature Matrix
| Feature | JSON | YAML | TOML | HOCON |
|---|---|---|---|---|
| Comments | ❌ No | ✅ Yes (#) |
✅ Yes (#) |
✅ Yes (# or //) |
| Data Types | String, Number, Boolean, Array, Object, Null | String, Number, Boolean, List, Dictionary, Custom | String, Integer, Float, Boolean, Date, Array, Table | All JSON types + Substitutions |
| Nesting | ✅ Unlimited | ✅ Unlimited | ✅ Unlimited | ✅ Unlimited |
| Multi-line Strings | ❌ Escaped | ✅ Native | ✅ Native (""") |
✅ Native |
| Arrays | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
| Date/Time Types | ❌ No | ✅ Yes | ✅ Yes | ❌ No (use strings) |
| Whitespace Sensitive | ❌ No | ✅ Yes | ❌ No | ❌ No |
| Trailing Commas | ❌ Error | ✅ Allowed | ✅ Allowed | ✅ Allowed |
| Parser Speed | ⚡ Very Fast | 🐢 Slower | ⚡ Fast | ⚡ Fast |
| Ecosystem Size | 🌟 Massive | 🌟 Large | 📊 Growing | 📊 JVM-focused |
Performance Benchmarks
Parsing 10,000 configuration files (1KB each):
| Format | Parse Time | Files/Second | Relative Speed |
|---|---|---|---|
| JSON | 0.34 seconds | ~29,400 | 1.0x (baseline) |
| HOCON | 0.42 seconds | ~23,800 | 0.81x |
| TOML | 0.89 seconds | ~11,200 | 0.38x |
| YAML | 1.24 seconds | ~8,000 | 0.27x |
Note: For most applications, configuration files are parsed once at startup, making these differences negligible.
Error Message Quality
JSON error:
1
SyntaxError: Unexpected token } in JSON at position 47
YAML error:
1
YAMLException: bad indentation of a mapping entry at line 5, column 3
TOML error:
1
Error: Expected string, got integer for key 'port' at line 12, column 8
HOCON error:
1
Error: Could not resolve substitution to a value: ${database.host} at line 15
TOML and HOCON provide specific error messages, making debugging easier.
Quick Decision Guide
Use JSON if:
- Building REST APIs
- Exchanging data between services
- Working with JavaScript in browsers
- Need maximum parsing speed
- Want universal compatibility
Use YAML if:
- Configuring Docker or Kubernetes
- Setting up CI/CD pipelines
- Need complex nested structures
- Human readability is critical
- Working with DevOps tools
Use TOML if:
- Building Rust or Python projects
- Need comments in config files
- Want unambiguous syntax
- Configuration changes frequently
- Date/time values are important
Use HOCON if:
- Building JVM applications (Akka, Play Framework)
- Need variable substitutions and includes
- Want configuration inheritance
- Complex microservices with shared configs
- Scala or Java projects
Real-World Usage Patterns
Web Application Stack:
- Docker Compose: YAML (orchestration)
- Application config: TOML (human-edited settings)
- package.json: JSON (package manifest)
- JVM services: HOCON (complex configs)
Python Project:
- pyproject.toml: TOML (project metadata)
- GitHub Actions: YAML (CI/CD)
- API responses: JSON (data exchange)
Microservices:
- Service communication: JSON
- Kubernetes manifests: YAML
- Application configs: TOML
- JVM microservices: HOCON
Parsing Examples
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
// JSON (built-in)
const config = JSON.parse(fs.readFileSync('config.json'));
// YAML: npm install js-yaml
const config = yaml.load(fs.readFileSync('config.yml'));
// TOML: npm install toml
const config = toml.parse(fs.readFileSync('config.toml'));
// HOCON: npm install config (for Node.js)
const config = require('config'); // Reads from config/default.conf
Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import json, yaml, tomli
from pyhocon import ConfigFactory
# JSON (built-in)
config = json.load(open('config.json'))
# YAML: pip install pyyaml
config = yaml.safe_load(open('config.yml'))
# TOML: pip install tomli
config = tomli.load(open('config.toml', 'rb'))
# HOCON: pip install pyhocon
config = ConfigFactory.parse_file('application.conf')
Converting Between Formats
Using remarshal (Python tool):
1
2
3
4
5
6
pip install remarshal
# Convert between any formats
remarshal -if json -of yaml config.json config.yml
remarshal -if yaml -of toml config.yml config.toml
remarshal -if toml -of json config.toml config.json
Watch out for:
- Comments are lost when converting to JSON
- HOCON substitutions need to be resolved before converting
- Data types may need manual adjustment
- TOML dates need special handling in JSON/YAML
Common Pitfalls
JSON:
- Trailing commas break parsing
- No comments allowed
- Multi-line strings are messy
- Fix: Use a linter
YAML:
- Indentation errors (spaces matter!)
- Boolean confusion (
nobecomesfalse) - Fix: Use consistent 2 or 4 spaces
TOML:
- Duplicate keys cause errors
- Complex nesting gets confusing
- Fix: Keep structure simple
HOCON:
- Substitution errors can be cryptic
- More complex than simple formats
- Fix: Test config loading early, use fallback values
Migration Guide
Quick migration steps:
- Audit your configs - List all JSON, YAML, TOML, HOCON files
- Categorize by use - APIs stay JSON, orchestration stays YAML
- Migrate one at a time - Start with most problematic file
- Add comments - Document why settings exist (TOML/YAML/HOCON)
- Test thoroughly - Ensure parsing works correctly
Simple migration example:
1
2
3
4
5
6
7
8
9
# Backup
cp config.json config.json.backup
# Convert
remarshal -if json -of toml config.json config.toml
# Add comments explaining settings
# Update code to parse TOML instead of JSON
# Test and deploy
Summary
Key Takeaways:
- JSON for machine-to-machine communication and APIs
- YAML for complex configs and DevOps tools
- TOML for human-edited application configurations
- HOCON for JVM applications with complex config needs
Migration advice:
- Don’t migrate everything at once
- Start with files that cause the most confusion
- Keep JSON for APIs and data exchange
- Use TOML for configs that need comments
- Follow ecosystem conventions (YAML for Docker, TOML for Rust, HOCON for Akka)
The right format depends on your specific needs. Consider who reads and writes the file, how complex the data is, and what your ecosystem uses.
Head-to-Head Comparisons
HOCON vs YAML
HOCON and YAML both support comments and human-readable syntax, but serve different ecosystems.
| Aspect | HOCON | YAML |
|---|---|---|
| Primary Use | JVM applications (Akka, Play) | DevOps tools (Docker, Kubernetes) |
| Syntax | Brace-based, JSON superset | Indentation-based |
| Comments | # and // |
# only |
| Variable Substitution | ✅ Native (${var}) |
❌ No (needs templating) |
| Config Includes | ✅ Built-in | ❌ No |
| Whitespace Sensitive | ❌ No | ✅ Yes |
| Ecosystem | Scala, Java | Universal |
Choose HOCON over YAML when:
- Building Scala/Java applications with Akka or Play Framework
- Need configuration inheritance and variable substitution
- Want to include shared config files
Choose YAML over HOCON when:
- Working with Docker, Kubernetes, or GitHub Actions
- Team is more familiar with YAML syntax
- Using tools that expect YAML (most DevOps tools)
HOCON vs JSON
HOCON is a superset of JSON, meaning all valid JSON is valid HOCON. HOCON adds features JSON lacks.
| Aspect | HOCON | JSON |
|---|---|---|
| Comments | ✅ Yes (#, //) |
❌ No |
| Trailing Commas | ✅ Allowed | ❌ Error |
| Unquoted Keys | ✅ Yes | ❌ Must quote |
| Variable Substitution | ✅ Yes (${var}) |
❌ No |
| Config Includes | ✅ Yes | ❌ No |
| Multi-line Strings | ✅ Yes | ❌ Escaped only |
| Ecosystem | JVM-focused | Universal |
Choose HOCON over JSON when:
- Need comments in configuration files
- Want cleaner syntax without excessive quotes
- Building JVM applications requiring config inheritance
- Complex configs with shared values
Choose JSON over HOCON when:
- Building REST APIs for data exchange
- Need maximum parsing speed
- Working in browser/JavaScript environments
- Want universal language support
TOML vs YAML
TOML and YAML are both human-friendly formats, but with different design philosophies.
| Aspect | TOML | YAML |
|---|---|---|
| Syntax Style | INI-like sections | Indentation-based |
| Whitespace Sensitive | ❌ No | ✅ Yes |
| Date/Time Types | ✅ Native | ✅ Via tags |
| Explicit Types | ✅ Very explicit | ⚠️ Can be ambiguous |
| Parsing Speed | ⚡ Faster | 🐢 Slower |
| Error Messages | ✅ Clear | ⚠️ Can be cryptic |
| Ecosystem | Rust, Python | DevOps, Kubernetes |
Choose TOML over YAML when:
- Building Rust applications (Cargo.toml)
- Creating Python projects (pyproject.toml)
- Want unambiguous parsing (no “Norway problem”)
- Need clear error messages
Choose YAML over TOML when:
- Configuring Docker Compose or Kubernetes
- Setting up CI/CD pipelines (GitHub Actions, GitLab CI)
- Complex nested structures are needed
- Working with DevOps tooling
TOML vs JSON
TOML is designed for human-edited configs, while JSON excels at machine-to-machine communication.
| Aspect | TOML | JSON |
|---|---|---|
| Comments | ✅ Yes (#) |
❌ No |
| Date/Time Types | ✅ Native | ❌ Strings only |
| Readability | ✅ Very clean | ⚠️ Verbose |
| Trailing Commas | ✅ Allowed | ❌ Error |
| Parsing Speed | ⚡ Fast | ⚡ Faster |
| Browser Support | ❌ Needs library | ✅ Native |
| Ecosystem | Rust, Python | Universal |
Choose TOML over JSON when:
- Need comments in config files
- Human-edited configuration
- Rust/Python projects (Cargo.toml, pyproject.toml)
- Date/time values are important
Choose JSON over TOML when:
- Building APIs for data exchange
- Need browser-native parsing
- Machine-generated configurations
- Maximum cross-platform compatibility
JSON vs YAML
JSON is strict and fast, YAML is flexible and readable.
| Aspect | JSON | YAML |
|---|---|---|
| Comments | ❌ No | ✅ Yes (#) |
| Syntax | Braces and brackets | Indentation-based |
| Whitespace Sensitive | ❌ No | ✅ Yes |
| Parsing Speed | ⚡ Very fast | 🐢 Slower |
| Multi-line Strings | ❌ Escaped | ✅ Native (\|, >) |
| Browser Support | ✅ Native | ❌ Needs library |
| Use Case | APIs, data exchange | Config files, DevOps |
Choose JSON over YAML when:
- Building REST APIs
- Data exchange between services
- Browser JavaScript applications
- Need strictest parsing
Choose YAML over JSON when:
- Configuring Docker, Kubernetes, or CI/CD
- Need comments in config files
- Complex nested structures
- Human readability is priority
YAML vs TOML
Both support comments, but differ in structure and ecosystem.
| Aspect | YAML | TOML |
|---|---|---|
| Indentation | ✅ Required | ❌ Not required |
| Sections | Implicit nesting | Explicit [section] |
| Parsing Speed | 🐢 Slower | ⚡ Faster |
| Error Clarity | ⚠️ Can be cryptic | ✅ Clear messages |
| Type Ambiguity | ⚠️ Yes (“Norway problem”) | ✅ No |
| Deep Nesting | ✅ Natural | ⚠️ Gets verbose |
Choose YAML over TOML when:
- Using Docker, Kubernetes, or Ansible
- Deep nesting is common
- Team knows YAML well
Choose TOML over YAML when:
- Want explicit, unambiguous syntax
- Rust or Python projects
- Flat/shallow configuration structures
Building scalable systems? Check out these related posts:
References: