Elk project heeft configuratie nodig, en je hebt drie hoofdopties: JSON, YAML of TOML. Ik heb alle drie uitgebreid gebruikt, en elk maakt me op een andere manier gek. Hier is mijn eerlijke mening over wanneer je wat moet gebruiken.

JSON: De universele soldaat

Je kent JSON al. Het is overal. Elke taal kan het parsen. Maar laten we eerlijk zijn over de zwaktes als configuratiebestand:

  • Geen commentaar. Je kunt letterlijk niet uitleggen wat een instelling doet. Dat is belachelijk voor een configuratiebestand.
  • Geen afsluitende komma's. Voeg een nieuwe regel toe aan het einde, vergeet de komma op de vorige regel — config kapot.
  • Overal aanhalingstekens. Elke sleutel heeft dubbele aanhalingstekens nodig: {"port": 8080} in plaats van gewoon port: 8080.

Desondanks wordt JSON voor configuratie gebruikt door npm (package.json), TypeScript (tsconfig.json), VS Code (settings.json) en meer. De reden? Nul ambiguïteit. JSON is zo strikt dat er maar één manier is om het te interpreteren.

YAML: Mooi maar gevaarlijk

YAML ziet er geweldig uit. Schoon, minimaal, leesbaar voor mensen. Maar het heeft beruchte valkuilen:

yaml

Dat is het "Noorwegen-probleem" dat ik in het YAML-artikel noemde. YAML 1.1 behandelt NO, YES, ON, OFF als booleans. Het is opgelost in YAML 1.2, maar veel parsers gebruiken nog steeds standaard het 1.1-gedrag.

Dan is er de inspringingsgevoeligheid. Eén verkeerd geplaatste spatie kan de betekenis van je bestand compleet veranderen — en de foutmelding zal verschrikkelijk zijn.

Dat gezegd hebbende, YAML is de standaard voor Docker Compose, Kubernetes, GitHub Actions en Ansible. Als je in de DevOps/cloud-wereld zit, is YAML-vaardigheid verplicht.

TOML: De configuratiespecialist

TOML (Tom's Obvious Minimal Language) is specifiek ontworpen voor configuratie. Het streeft ernaar om vanzelfsprekend te zijn — wat betekent dat er (bijna) geen manier is om een TOML-bestand verkeerd te lezen.

Zo ziet een TOML-config eruit:

toml

Wat TOML echt goed doet: native datum/tijd-types (created = 2026-03-08T10:30:00Z), geen inspringingsproblemen, commentaar met #, en een syntaxis die oprecht moeilijk te verpesten is.

Het nadeel? Diep geneste structuren worden verbose. TOML is geweldig voor vlakke configs, maar als je vijf niveaus van nesting nodig hebt, zijn YAML of JSON misschien leesbaarder.

TOML wordt gebruikt door Rust (Cargo.toml), Python (pyproject.toml), Hugo, en een groeiend aantal tools.

Mijn aanbevelingen

ScenarioKeuzeWaarom
Machine-gegenereerde configJSONStrictst en meest compatibel
DevOps/infrastructuurYAMLEcosysteem verwacht het
App-configuratieTOMLCommentaar + minimale ambiguïteit
Eenvoudige sleutel-waarde instellingenTOMLSchoonste voor vlakke configs
Complexe geneste structurenYAMLBeste geneste syntaxis

Conclusie

Dezelfde config in alle drie formaten

Dezelfde data in alle drie formaten zien maakt de verschillen echt duidelijk. Hier is een eenvoudige app-configuratie:

JSON:

json

YAML:

yaml

TOML:

toml

Merk op hoe JSON al die aanhalingstekens en accolades nodig heeft, YAML het beknoptst is maar afhankelijk van inspringing, en TOML een middenweg vindt met expliciete sectiekoppen en geen inspringsafhankelijkheid.

Veelgemaakte fouten in configuratiebestanden

YAML: Onbedoelde typeconversie. Naast het Noorwegen-probleem interpreteert YAML ook 3.10 als het getal 3.1 (de afsluitende nul verdwijnt), en 1_000 als 1000. Als je configwaarden versiestrings zijn zoals 3.10, zet ze dan altijd tussen aanhalingstekens: version: "3.10".

JSON: Vergeten dat volgorde niet uitmaakt. JSON-objecten zijn ongeordend volgens de specificatie. Als je configverwerking afhankelijk is van sleutelvolgorde, bouw je op een fragiel fundament. Sommige parsers behouden de invoegvolgorde, andere niet.

TOML: Verwarring met arrays van tabellen. TOML gebruikt [[dubbele.haken]] voor arrays van tabellen, wat beginners laat struikelen. Zo definieer je meerdere servers:

toml

Omgevingsspecifieke configs

Een gebied waar alle drie formaten moeite mee hebben is het omgaan met omgevingsspecifieke overschrijvingen (dev vs staging vs productie). Veelvoorkomende oplossingen zijn:

  • Meerdere bestanden: config.base.yaml + config.production.yaml met diepe samenvoeging
  • Omgevingsvariabele-interpolatie: Sommige YAML-tools ondersteunen ${DB_HOST}-syntaxis, maar het is niet standaard
  • Externe tools: Doppler, HashiCorp Vault, of cloud-native configuratiediensten

Persoonlijk kies ik voor een eenvoudige aanpak: één configuratiebestand met verstandige standaardwaarden, en omgevingsvariabelen voor alles wat verschilt tussen omgevingen. Alle drie formaten kunnen omgevingsvariabelen lezen op applicatieniveau, dus het configuratieformaat zelf hoeft geen interpolatie te ondersteunen.

Formaatpopulariteit per ecosysteem

EcosysteemPrimair formaatBekende voorbeelden
JavaScript/Node.jsJSONpackage.json, tsconfig.json, .eslintrc.json
PythonTOMLpyproject.toml, Cargo.toml (Rust)
DevOps/CloudYAMLdocker-compose.yml, k8s manifesten, GitHub Actions
GoTOML/YAMLBeide veelgebruikt, geen enkele standaard
.NETJSONappsettings.json (verving XML-gebaseerde web.config)

Conclusie

Er is geen enkel juist antwoord. Gebruik JSON wanneer je maximale compatibiliteit nodig hebt. Gebruik YAML wanneer het ecosysteem het vereist (Kubernetes gaat geen TOML accepteren). Gebruik TOML wanneer je commentaar en minimale verrassingen wilt.

JSONC en JSON5: JSON met zijwieltjes

Oké, we hebben JSON dus bekritiseerd omdat het geen commentaar en afsluitende komma's ondersteunt. Maar hier is wat niemand je vertelt: er bestaan eigenlijk varianten van JSON die deze ergernissen oplossen, en je hebt er waarschijnlijk al een gebruikt zonder het te beseffen.

Eerst: JSONC (JSON with Comments). Als je ooit een tsconfig.json hebt geopend en dacht "wacht, er staat commentaar in... ik dacht dat JSON geen commentaar toestond?" — ja, dat is JSONC. Het is eigenlijk JSON maar je kunt // enkelregelig commentaar en /* */ blokcommentaar gebruiken. VS Code gebruikt JSONC voor zijn settings.json, launch.json en keybindings.json bestanden. TypeScript's tsconfig.json is ook technisch JSONC, geen strikt JSON.

Zo ziet JSONC eruit:

jsonc

Dan is er JSON5, dat nog verder gaat. JSON5 versoepelt een aantal van JSON's strengste regels:

  • Strings met enkele aanhalingstekens: {'name': 'Sarah'} — eindelijk!
  • Afsluitende komma's: {"a": 1, "b": 2,} — geen diff-ruis meer
  • Sleutels zonder aanhalingstekens (als het geldige identifiers zijn): {name: "Sarah"}
  • Hexadecimale getallen: 0xFF
  • Meerregelige strings met backslash-voortzetting
  • Infinity, -Infinity en NaN als geldige getallen

JSON5 is geweldig voor configuratiebestanden waar mensen het hoofdpubliek zijn. Sommige tools ondersteunen het native — bijvoorbeeld Babel's .babelrc kan JSON5 zijn. Maar gebruik JSON5 niet voor API-responses of gegevensuitwisseling. Het hele punt van strikt JSON is dat iedereen het eens is over het formaat. JSON5 is voor mensen, niet voor machines.

Mijn vuistregel: als een tool JSONC of JSON5 ondersteunt, gebruik het. Er is letterlijk geen nadeel aan commentaar in je configuratiebestanden. Maar als je een bibliotheek schrijft die config leest, ondersteun dan eerst standaard JSON en JSONC/JSON5 als optionele extra's.

YAML-ankers en aliassen: DRY config

Hier komt YAML's echte killer feature dat veel ontwikkelaars nooit ontdekken: ankers en aliassen. Ze laten je een blok config één keer definiëren en overal hergebruiken. In feite DRY (Don't Repeat Yourself) voor configuratiebestanden.

Een anker wordt gemarkeerd met &naam en een alias verwijst ernaar met *naam. Hier is een praktisch Docker Compose-voorbeeld:

yaml

Zie je wat er gebeurde? We definieerden gemeenschappelijke instellingen één keer met &common en haalden ze binnen in drie services met <<: *common. Zonder ankers zou je dat herstart-beleid en die logging-config in elke service kopiëren en plakken. En wanneer je de logrotatie moet wijzigen? Eén plek in plaats van twaalf.

Je kunt ankers ook gebruiken voor eenvoudigere waarden — niet alleen voor maps:

yaml

Nu de valkuilen. De merge-sleutel << die ankers echt krachtig maakt? Die is eigenlijk geen onderdeel van de YAML 1.2-specificatie. Het was een YAML 1.1-type-extensie, en hoewel de meeste populaire parsers het nog ondersteunen, is het technisch niet standaard. Dus als je een strikte YAML 1.2-parser gebruikt, werken merge-sleutels mogelijk niet.

Bovendien werken ankers alleen binnen één bestand. Je kunt geen anker refereren dat in een ander YAML-bestand is gedefinieerd. En de foutmeldingen als je een aliasnaam verkeerd typt? Meestal iets als "undefined alias" zonder enige context over waar het anker zou moeten zijn. Typisch YAML.

TOML diepgaand: Geavanceerde functies

De meeste mensen kennen de TOML-basis — secties met [haken], sleutel-waardeparen, commentaar met #. Maar TOML heeft een aantal echt coole functies die niet genoeg aandacht krijgen. Laat me je er doorheen leiden.

Puntsleutels laten je geneste structuren definiëren zonder sectiekoppen:

toml

Dit is handig wanneer je maar een paar geneste waarden hebt en geen hele sectie wilt aanmaken.

Inline-tabellen geven je JSON-achtige compacte syntaxis voor kleine objecten:

toml

Gebruik inline-tabellen spaarzaam — ze kunnen niet over meerdere regels gaan en worden snel onleesbaar als je er te veel in stopt.

Meerregelige strings komen in twee varianten, en hier wordt TOML verrassend doordacht:

toml

De drievoudig aangehaalde basisstring (""") verwerkt escape-sequenties, terwijl de letterlijke versie (''') alles als ruwe tekst behandelt. Dit is perfect voor regex-patronen of Windows-bestandspaden waar je niet wilt dat backslashes als escapes worden geïnterpreteerd.

Native datum/tijd-types zijn iets dat noch JSON noch YAML zo schoon afhandelt:

toml

Dit zijn eersteklas types in TOML, geen strings die doen alsof ze datums zijn. Je TOML-parser geeft je echte datum/tijd-objecten, geen strings die je zelf moet parsen.

Waarom koos Rust's Cargo voor TOML? Omdat Cargo-configs precies de sweet spot voor TOML zijn: gematigd genest, door mensen bewerkt, heeft commentaar nodig, en profiteert van strikt typen. Je wilt niet dat je afhankelijkheidsversies stilletjes als floats worden geïnterpreteerd (ik kijk naar jou, YAML).

Beveiligingsoverwegingen

Oké, dit is de sectie waar het een beetje eng wordt. Als je configuratiebestanden laadt van onbetrouwbare bronnen — of zelfs als je denkt dat je dat niet doet — moet je weten over deserialisatie-aanvallen.

Het schoolvoorbeeld hiervan is PyYAML's yaml.load(). In oudere versies kon deze onschuldig ogende functie willekeurige Python-code uitvoeren die in een YAML-bestand was ingebed. Ik maak geen grapje. Kijk hier:

yaml

Als iemand dit in een YAML-config smokkelt en je Python-app het laadt met yaml.load() in plaats van yaml.safe_load(), zal het letterlijk dat systeemcommando uitvoeren. Alles verwijderen. Een achterdeur installeren. Wat de aanvaller maar wil.

De oplossing is heel simpel — gebruik altijd yaml.safe_load() in Python:

python

De PyYAML-documentatie waarschuwt hier nu voor, en nieuwere versies tonen een deprecatiewaarschuwing als je yaml.load() gebruikt zonder een Loader op te geven. Maar er zijn nog steeds talloze tutorials en Stack Overflow-antwoorden die de onveilige versie tonen. Het is een landmijn die in het volle zicht verstopt is.

Dan is er de "billion laughs"-aanval (ook wel XML-bom genoemd, maar het werkt ook met YAML). Het idee is recursieve expansie — je definieert entiteiten die andere entiteiten refereren, waardoor exponentiële groei ontstaat:

yaml

Elk niveau vermenigvuldigt de data met 5, dus bij niveau 8 of 9 kijk je naar gigabytes aan data uit een paar regels YAML. De meeste moderne YAML-parsers hebben diepte- en expansielimieten om dit te voorkomen, maar het is de moeite waard om te weten.

JSON is inherent veiliger omdat het geen uitvoeringssemantiek heeft. Er is geen manier om code in te bedden, geen typeconstructors, geen entiteitsexpansie. Een JSON-parser leest gewoon data — strings, getallen, booleans, arrays en objecten. Dat is alles. Dit is een van de ondergewaardeerde voordelen van JSON's eenvoud. Wanneer beveiliging belangrijk is, is JSON's gebrek aan functies eigenlijk een functie.

TOML is ook vrij veilig — het heeft geen code-uitvoeringsmogelijkheden en geen recursieve expansie. Maar het is minder getest in de praktijk dan JSON-parsers, dus houd je TOML-bibliotheken up-to-date.

Conclusie: als je configuratiebestanden van gebruikers of externe bronnen accepteert, is JSON de veiligste keuze. Als je YAML moet gebruiken, gebruik dan altijd veilige laadfuncties en overweeg een YAML-linter te draaien om verdachte constructies te detecteren.

Praktijkvoorbeelden van configuratiebestanden

Theorie is mooi, maar laten we echte configuratiebestanden bekijken die je in het wild tegenkomt. Ik heb elk geannoteerd om formaatspecifieke patronen te benadrukken.

GitHub Actions workflow (YAML):

yaml

Hier schittert YAML echt. De GitHub Actions workflow-syntaxis zou pijnlijk zijn in JSON — al die geneste lijsten en de op inspringing gebaseerde structuur past van nature bij YAML. Je kunt ook zien hoe commentaar helpt de matrix-strategie uit te leggen.

Rust's Cargo.toml (TOML):

toml

Merk op hoe het Cargo-manifest inline-tabellen gebruikt voor afhankelijkheden met features. De [[bin]] dubbele-haken-syntaxis definieert een array van tabellen — elke [[bin]]-invoer voegt een ander binary-doel toe. TOML houdt dit vlak en leesbaar zonder inspringingsspelletjes.

package.json (JSON):

json

De klassieker. Geen commentaar, veel aanhalingstekens, maar elke tool op de planeet kan het lezen. Wat package.json laat werken ondanks JSON's beperkingen is dat het schema zo bekend is — je hebt geen commentaar nodig om uit te leggen wat scripts.build doet, want elke JavaScript-ontwikkelaar weet het al.

.prettierrc (JSON):

json

Eenvoudige, vlakke sleutel-waarde config. Eerlijk gezegd zou dit iets mooier zijn als TOML (commentaar!) of zelfs JSONC, en Prettier ondersteunt andere formaten. Maar JSON is de standaard, en voor iets zo eenvoudigs maakt het nauwelijks uit.

Migratie tussen formaten

Je hebt dus besloten dat het configuratieformaat van je project een vergissing was en je wilt wisselen. Of misschien haal je config op van een tool die een ander formaat gebruikt. Hoe dan ook, je moet converteren tussen TOML, YAML en JSON. Ik zeg je, het gaat niet altijd zo soepel als je hoopt.

Conversietools:

  • yq — Het Zwitsers zakmes voor YAML. Kan converteren tussen YAML, JSON, TOML en XML. yq -o=json config.yaml geeft je JSON-uitvoer. Het is als jq maar dan voor YAML.
  • toml-cli en taplo — Commandoregel TOML-processors. Taplo is bijzonder goed voor TOML-formattering en -validatie.
  • Python-oneliners — In noodgevallen converteert python -c "import yaml, json, sys; print(json.dumps(yaml.safe_load(sys.stdin), indent=2))" < config.yaml YAML naar JSON. Niet fraai, maar het werkt.
  • Online converters — Voor snelle eenmalige conversies kunnen onze formatters helpen. Plak je config in de juiste formatter, ruim het op en herschrijf het handmatig in het doelformaat.

Wanneer migreren:

  • Je team blijft YAML-inspringsfouten maken in CI-configs? Misschien vermindert overstappen naar TOML die hoofdpijn.
  • Je hebt commentaar nodig in een JSON-configbestand? Overweeg migratie naar JSONC (als de tool het ondersteunt) of TOML.
  • Je bouwt een nieuw Rust/Python-project en het ecosysteem verwacht TOML? Verzet je niet — ga voor TOML.

Veelvoorkomende valkuilen bij migratie:

YAML's impliciete datumparsing zal je verrassen. Als je een YAML-waarde hebt zoals release: 2024-03-08, interpreteert YAML dat als een datumobject, niet een string. Converteer dat naar JSON en je krijgt misschien "release": "2024-03-08T00:00:00Z" of "release": "2024-03-08", afhankelijk van je converter. Test altijd je uitvoer.

TOML's strikte typing betekent dat je geen arrays met gemengde types kunt hebben. In JSON en YAML is [1, "two", true] prima. In TOML moet elk element in een array hetzelfde type zijn. Als je brondata gemengde arrays heeft, moet je herstructureren.

En hier is de grote: commentaar gaat verloren. JSON ondersteunt geen commentaar, dus als je converteert van TOML of YAML naar JSON, verdwijnt al je zorgvuldig geschreven commentaar. De andere kant op (JSON naar YAML/TOML) wil je handmatig commentaar toevoegen om niet-voor-de-hand-liggende instellingen uit te leggen, want de JSON-versie had ze zeker niet.

Nog iets — YAML's ankers en aliassen hebben geen equivalenten in JSON of TOML. Als je YAML-config afhankelijk is van ankers voor DRY config, betekent converteren naar een ander formaat dat je die gedeelde blokken handmatig moet dupliceren. Dat kan een aanzienlijke uitbreiding in bestandsgrootte betekenen voor complexe configs.

Probeer het zelf

Welk formaat je ook kiest, het goed geformatteerd houden van je configs maakt ze makkelijker te beoordelen en te debuggen. Onze TOML Formatter ruimt rommelige TOML-bestanden direct op. De YAML Formatter repareert inspringingsproblemen voordat ze problemen veroorzaken. En de JSON Formatter maakt zelfs diep geneste JSON-configs in één oogopslag leesbaar.