Open een Rust-project en je vindt een Cargo.toml. Kloon een GitHub-repository en
je vindt een map .github/workflows/ vol YAML. Beide formaten doen dezelfde oppervlakkige
taak — gestructureerde configuratie opslaan die mensen bewerken — maar ze maken heel verschillende
afwegingen. Als je ooit YAML stilletjes een waarde hebt zien verminken, of je je hebt afgevraagd waarom
Rust voor zijn pakketmanifest TOML koos in plaats van YAML, dan is dit het artikel voor jou.
Het Kernverschil: Expliciet vs Impliciet
De fundamentele splitsing tussen TOML en YAML gaat over hoeveel de parser mag raden.
TOML is expliciet: elke waarde heeft
een ondubbelzinnig type. Strings zijn altijd tussen aanhalingstekens. Booleans zijn precies true
of false. Datetimes zijn eersteklas waarden met hun eigen syntaxis. Er is geen impliciete
typecoercitie — de parser probeert niet slim te zijn.
YAML gaat de andere kant op.
Het probeert gemakkelijk te zijn: je hebt geen aanhalingstekens nodig om de meeste strings, en de parser
leidt typen af uit het uiterlijk van de waarde. Die gevolgtrekking is wat mensen bijt. De YAML 1.1-specificatie
(nog steeds gebruikt door veel hulpmiddelen) behandelt yes, no, on,
off, true en false allemaal als booleans. Het behandelt ongeciteerde
versietekenreeksen zoals 1.0 als floats. En dan is er het Noorwegenprobleem.
De Beroemde Valkuilen van YAML
Het Noorwegenprobleem werd een soort meme in DevOps-kringen. In YAML 1.1 wordt de landcode
NO geparseerd als boolean false. Dus een configuratiemapping van ISO-landcodes
naar instellingen zou de inzending van Noorwegen stilletjes omzetten in een boolean. De
YAML 1.2-specificatie heeft dit
opgelost, maar veel veelgebruikte parsers — waaronder PyYAML's standaardmodus tot onlangs — zijn nog steeds
gericht op YAML 1.1. Controleer welke specificatieversie je tooling eigenlijk implementeert voordat je
het vertrouwt.
NO,
Yes, on en off allemaal booleans. De oplossing is eenvoudig —
citeer je strings — maar het probleem is dat het stil is. Je configuratie laadt zonder fout en je
krijgt een boolean waar je een string verwachtte.# YAML 1.1 implicit type coercion — all of these silently become booleans:
countries:
norway: NO # → false ← The Norway Problem
sweden: SE # → "SE" (fine, not in the boolean list)
enabled: yes # → true
disabled: no # → false
feature_flag: on # → true
another: off # → false
# Version strings can become numbers:
python_version: 3.10 # → float 3.1 (trailing zero dropped)
api_version: 1.0 # → float 1.0
# Safe: quote anything that could be ambiguous
python_version: "3.10"
country: "NO"
enabled: "yes"- Octale literals. In YAML 1.1 wordt
010geparseerd als8(octaal), niet10. Dit is belangrijk voor bestandsrechtenwaarden zoals0755. - Tab vs spatie. YAML verbiedt tab-tekens voor inspringing. Plak code in van een editor die is geconfigureerd om tabs te gebruiken en je krijgt een cryptische parseerfout — of erger, stille uitlijning.
- Impliciete nulls. Een sleutel zonder waarde wordt
null. Gemakkelijk per ongeluk te maken bij handmatig bewerken. - Inspringingsgevoeligheid. Eén extra spatie en een waarde verschuift stilletjes van het ene bovenliggende element naar het andere. Geen fout, alleen verkeerde gegevens.
TOML: Hoe Expliciete Typen Er Eigenlijk Uitzien
Het typesysteem van TOML is beschreven in de
TOML v1.0-specificatie. Strings
moeten tussen aanhalingstekens staan (enkel of dubbel). Booleans zijn true of false
en niets anders. Integers zijn integers. Floats zijn floats. En datetimes — iets dat noch JSON noch YAML
als native type heeft — zijn eersteklas waarden in TOML.
# TOML types are always unambiguous
name = "my-app" # string — must be quoted
version = "1.0.0" # string — quotes make it clear this is not a float
port = 8080 # integer
debug = false # boolean — only true/false, nothing else
threshold = 0.95 # float
# Datetime is a first-class type — no string parsing needed
created_at = 2024-01-15T09:30:00Z
build_date = 2024-03-20
# Arrays
allowed_hosts = ["localhost", "staging.example.com", "api.example.com"]
# Inline tables
[database]
host = "postgres.internal"
port = 5432
name = "payments_prod"
pool_size = 20De landcode NO is gewoon een string in TOML omdat het niet tussen aanhalingstekens staat,
maar TOML leidt geen typen af uit stringachtige waarden — ongeciteerde waarden volgen strikte syntactische
regels. Om een string te zijn heeft het aanhalingstekens nodig. TOML heeft simpelweg niet het impliciete
coercitemechanisme dat YAML's valkuilen veroorzaakt.
Naast Elkaar: Een CI-Pijplijnconfiguratie
Hier is een GitHub Actions-workflow — dit is het thuisterrein van YAML. Het formaat past van nature omdat GitHub Actions YAML verwacht, de op inspringing gebaseerde nesting de logische structuur weerspiegelt en opmerkingen essentieel zijn voor het uitleggen van niet-voor-de-hand-liggende stepconfiguraties.
# .github/workflows/release.yml
name: Release
on:
push:
tags:
- 'v*'
jobs:
build-and-publish:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build --if-present
- name: Publish to npm
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}Hier is het equivalente projectmanifest in TOML — dit is waar TOML uitblinkt. Vergelijk een echte Cargo.toml-structuur met hoe dezelfde configuratie er in YAML uit zou zien:
# Cargo.toml — Rust package manifest
[package]
name = "payments-service"
version = "2.4.1"
edition = "2021"
description = "Payment processing microservice"
license = "MIT"
authors = ["Alice Chen <[email protected]>"]
[dependencies]
tokio = { version = "1.35", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sqlx = { version = "0.7", features = ["postgres", "runtime-tokio-native-tls"] }
tracing = "0.1"
anyhow = "1.0"
[dev-dependencies]
tokio-test = "0.4"
wiremock = "0.6"
[profile.release]
opt-level = 3
lto = true
codegen-units = 1De Zwakke Punten van TOML: Array van Tabellen
TOML is niet zonder ruwe kanten. De syntaxis voor arrays van tabellen — [[dubbele haakjes]]
— is een van de verwarrendste dingen in de specificatie. Het is hoe je uitdrukt wat JSON zou schrijven als
een array van objecten, en het leest aanvankelijk vreemd.
# TOML array of tables — [[double brackets]] creates array entries
[[server]]
host = "web-01.example.com"
port = 443
region = "us-east-1"
[[server]]
host = "web-02.example.com"
port = 443
region = "us-west-2"
[[server]]
host = "web-03.example.com"
port = 443
region = "eu-west-1"
# The above is equivalent to this JSON:
# { "server": [
# { "host": "web-01.example.com", "port": 443, "region": "us-east-1" },
# { "host": "web-02.example.com", "port": 443, "region": "us-west-2" },
# { "host": "web-03.example.com", "port": 443, "region": "eu-west-1" }
# ]}In YAML leest dezelfde structuur natuurlijker — gewoon een lijst met ingesprongen eigenschappen.
Voor diep geneste gegevens met herhaalde arrayinvoer is de op inspringing gebaseerde nesting van YAML
genuien minder uitgebreid dan TOML's [[sectie]]-headers. TOML heeft ook geen equivalent
voor YAML's anker-en-alias-systeem, dus je kunt geen gedeeld blok eenmalig definiëren en er elders naar
verwijzen. Als je dezelfde set waarden in meerdere TOML-tabellen dupliceert, moet je ze handmatig kopiëren.
Waar Elk Formaat in de Praktijk Wint
YAML wint standaard in deze ecosystemen:
- GitHub Actions. De gehele workflowsyntaxis is YAML. Je hebt hier geen keuze, en dat is prima — de inspringing mapt goed op de geneste structuur van jobs, stappen en condities.
- Kubernetes. Elk manifest — Deployments, Services, ConfigMaps, Ingress-regels — is YAML. Het Kubernetes-objectmodel is diep genest, en YAML verwerkt dat gracieus.
- Docker Compose. Servicedefinities, netwerken, volumes — allemaal YAML. Opmerkingen die uitleggen waarom een poort wordt blootgesteld of waarom een specifiek healthcheck-interval wordt gebruikt, zijn deel van de documentatie.
- Ansible. Playbooks, rollen, variabelebestanden — overal YAML. De ondersteuning voor opmerkingen wordt actief gebruikt om niet-voor-de-hand-liggende taakparameters uit te leggen.
TOML wint wanneer je het formaat controleert:
- Rust-projecten.
Cargo.tomlis de gouden standaard. Afhankelijkheidsdeclaraties, functiemarkeringen, build-profielen — allemaal in TOML. De expliciete typen betekenen dat versiestrings zoals"1.0.0"strings blijven. - Python-projecten.
pyproject.tomlis de standaard geworden voor Python-projectmetadata, build-configuratie en toolinstellingen (Black, isort, mypy, pytest lezen er allemaal uit). - Toolconfiguratie waar ambiguïteit bugs veroorzaakt. Als je configuratie versiestrings, landcodes of andere waarden bevat die eruitzien alsof ze booleans of getallen zouden kunnen zijn, elimineren TOML's expliciete typen een hele categorie parseringverrassingen.
- Platte configuraties. Wanneer je configuratie voornamelijk sleutel-waardeparen op één of twee niveaus van nesting bestaat, is TOML leesbaarder dan YAML en schoner dan JSON.
De Praktische Beslissingsgids
Het merendeel van de tijd wordt de keuze voor je gemaakt door het ecosysteem. GitHub Actions is YAML. Kubernetes is YAML. Rust is TOML. Python-tooling is TOML. Wanneer je echt een vrije keuze hebt, gebruik dit als je gids:
- Gebruik YAML wanneer de tooling het verplicht stelt — CI/CD-platforms, Kubernetes, Helm-grafieken, Docker Compose, Ansible. Vechten tegen de conventie kost meer dan het bespaart.
- Gebruik YAML wanneer je ankers en aliassen nodig hebt om een complexe configuratie DRY te houden — er is geen TOML-equivalent.
- Gebruik TOML voor projectmanifesten en toolingconfiguratie die je beheert — pakketmetadata, linter-instellingen, build-configuratie.
- Gebruik TOML wanneer je configuratie waarden bevat die YAML 1.1 mogelijk verkeerd interpreteert — versiestrings, landcodes, alles wat op een boolean of getal lijkt.
- Citeer je YAML-strings wanneer de waarde verward kan worden met een boolean, getal of null.
Vooral: versienummers, landcodes, alles dat begint met een cijfer, en waarden zoals
yes,no,on,off.
Werken met Beide Formaten
Moet je een configuratiebestand valideren of mooi afdrukken waaraan je werkt? De TOML Formatter verwerkt TOML-bestanden en de YAML Formatter dekt YAML. Als je een configuratie van het ene formaat naar het andere moet migreren — zeg, het converteren van een YAML-gebaseerde toolconfiguratie naar TOML voor een project dat er de voorkeur aan geeft — produceren de TOML naar JSON- en YAML naar JSON-converters beide JSON, dat je vervolgens naar je doelformaat kunt converteren. Soms is het gaan via JSON als tussenstap het meest betrouwbare pad.
Één ding dat de moeite waard is om te weten: beide formaten hebben hun eigen JSON-compatibele subsets. Geldige JSON is geldige YAML (YAML is een superset van JSON). TOML heeft die relatie niet met JSON, maar de TOML-specificatie is bewust eenvoudig gehouden — het is een korte lezing, wat meer is dan je kunt zeggen voor de volledige YAML 1.2-specificatie.
Samenvatting
TOML en YAML concurreren niet echt. Ze hebben zich gevestigd in verschillende niches op basis van hun ontwerprioriteiten. YAML's impliciete typen en op inspringing gebaseerde structuur maken het de natuurlijke keuze voor grote, geneste configuraties die door teams worden onderhouden — denk aan Kubernetes-manifesten en GitHub Actions-workflows. TOML's expliciete typen en platte sectiestructuur maken het de natuurlijke keuze voor projectmanifesten en toolconfiguratie waarbij een verkeerd gelezen versiestring of landcode een echte bug zou veroorzaken.
Het ene ding om mee te nemen: als je YAML schrijft en je waarden hebt die eruit kunnen zien als
booleans, getallen of nulls — citeer ze dan. Het is de ene gewoonte die de meeste YAML-gerelateerde
configuratiebugs voorkomt. En als je een nieuw project start en vrij je configuratieformaat kunt kiezen,
is TOML de moeite waard om eens naar te kijken. Het
TOML GitHub-repository
heeft goede voorbeelden, en als je eenmaal een Cargo.toml of pyproject.toml
hebt geschreven, wordt de aantrekkingskracht van het formaat vrij duidelijk.