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.

Het Noorwegenprobleem in de praktijk: in YAML 1.1 worden ongeciteerde 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
# 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 010 geparseerd als 8 (octaal), niet 10. Dit is belangrijk voor bestandsrechtenwaarden zoals 0755.
  • 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
# 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 = 20

De 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.

yaml
# .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:

toml
# 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 = 1

De 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
# 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.toml is 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.toml is 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.