Ouvrez n'importe quel projet Rust et vous trouverez un Cargo.toml. Clonez un dépôt GitHub et vous découvrirez un dossier .github/workflows/ rempli de YAML. Les deux formats accomplissent le même travail de surface — stocker une configuration structurée que les humains modifient — mais ils font des compromis très différents. Si vous avez déjà eu du YAML qui altère silencieusement une valeur, ou si vous vous êtes demandé pourquoi Rust a choisi TOML plutôt que YAML pour son manifeste de paquet, cet article est fait pour vous.

La différence fondamentale : explicite vs implicite

La différence essentielle entre TOML et YAML concerne la latitude laissée au parseur pour deviner. TOML est explicite : chaque valeur a un type non ambigu. Les chaînes sont toujours entre guillemets. Les booléens sont exactement true ou false. Les datetimes sont des valeurs de première classe avec leur propre syntaxe. Il n'y a pas de coercition de type implicite — le parseur ne cherche pas à être intelligent.

YAML penche de l'autre côté. Il essaie d'être pratique : vous n'avez pas besoin de guillemets autour de la plupart des chaînes, et le parseur déduit les types à partir de l'apparence des valeurs. C'est cette inférence qui piège les gens. La spécification YAML 1.1 (encore utilisée par de nombreux outils) traite yes, no, on, off, true et false comme des booléens. Elle interprète les chaînes de version non quotées comme 1.0 comme des flottants. Et puis il y a le problème de la Norvège.

Les pièges célèbres de YAML

Le problème de la Norvège est devenu un mème dans les cercles DevOps. En YAML 1.1, le code pays NO est parsé comme le booléen false. Ainsi, une configuration mappant des codes de pays ISO à des paramètres convertirait silencieusement l'entrée de la Norvège en booléen. La spécification YAML 1.2 a corrigé cela, mais de nombreux parseurs largement utilisés — dont le mode par défaut de PyYAML jusqu'à récemment — ciblent encore YAML 1.1. Vérifiez quelle version de spécification votre outillage implémente réellement avant de lui faire confiance.

Le problème de la Norvège en pratique : En YAML 1.1, NO, Yes, on et off non quotés deviennent tous des booléens. La solution est simple — mettre les chaînes entre guillemets — mais le problème est que c'est silencieux. Votre configuration se charge sans erreur et vous obtenez un booléen là où vous attendiez une chaîne.
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"
  • Littéraux octaux. En YAML 1.1, 010 est parsé comme 8 (octal), pas 10. Cela importe pour les valeurs de permissions de fichiers comme 0755.
  • Tabulation vs espace. YAML interdit les caractères de tabulation pour l'indentation. Collez du code depuis un éditeur configuré pour utiliser des tabulations et vous obtenez une erreur de parsage cryptique — ou pire, un décalage silencieux.
  • Nulls implicites. Une clé sans valeur devient null. Facile à créer accidentellement lors d'une édition manuelle.
  • Sensibilité à l'indentation. Un espace supplémentaire et une valeur se déplace silencieusement d'un parent à un autre. Pas d'erreur, juste des données incorrectes.

TOML : à quoi ressemblent vraiment les types explicites

Le système de types de TOML est détaillé dans la spécification TOML v1.0. Les chaînes doivent être entre guillemets (simples ou doubles). Les booléens sont true ou false et rien d'autre. Les entiers sont des entiers. Les flottants sont des flottants. Et les datetimes — quelque chose que ni JSON ni YAML n'a comme type natif — sont des valeurs de première classe en 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

Le code pays NO est simplement une chaîne en TOML parce qu'il n'est pas entre guillemets, mais TOML n'infère pas les types à partir de valeurs ressemblant à des chaînes — les valeurs non quotées suivent des règles syntaxiques strictes. Pour être une chaîne, des guillemets sont nécessaires. TOML n'a tout simplement pas le mécanisme de coercition implicite qui cause les pièges de YAML.

Côte à côte : une configuration de pipeline CI

Voici un workflow GitHub Actions — c'est le terrain de prédilection de YAML. Le format s'adapte naturellement car GitHub Actions attend du YAML, l'imbrication basée sur l'indentation reflète la structure logique, et les commentaires sont essentiels pour expliquer les configurations d'étapes non évidentes.

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 }}

Voici maintenant le manifeste de projet équivalent en TOML — c'est là que TOML brille. Comparez une vraie structure Cargo.toml avec ce à quoi la même configuration ressemblerait en YAML :

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

Les faiblesses de TOML : tableau de tables

TOML n'est pas sans aspérités. La syntaxe pour les tableaux de tables — [[double brackets]] — est l'une des choses les plus déroutantes dans la spécification. C'est ainsi que vous exprimez ce que JSON écrirait comme un tableau d'objets, et cela semble étrange au premier abord.

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" }
# ]}

En YAML, la même structure se lit plus naturellement — juste une liste avec des propriétés indentées. Pour des données profondément imbriquées avec des entrées de tableau répétées, l'imbrication basée sur l'indentation de YAML est véritablement moins verbeuse que les en-têtes [[section]] de TOML. TOML n'a également pas d'équivalent au système d'ancres et d'alias de YAML, vous ne pouvez donc pas définir un bloc partagé une fois et le référencer ailleurs. Si vous vous retrouvez à dupliquer le même ensemble de valeurs dans plusieurs tables TOML, vous êtes obligé de les copier manuellement.

Où chaque format gagne en pratique

YAML gagne par défaut dans ces écosystèmes :

  • GitHub Actions. La totalité de la syntaxe de workflow est en YAML. Vous n'avez pas le choix ici, et c'est bien — l'indentation correspond bien à la structure imbriquée des jobs, étapes et conditions.
  • Kubernetes. Chaque manifeste — Deployments, Services, ConfigMaps, règles Ingress — est en YAML. Le modèle objet Kubernetes est profondément imbriqué, et YAML gère cela avec élégance.
  • Docker Compose. Définitions de services, réseaux, volumes — tout en YAML. Les commentaires expliquant pourquoi un port est exposé ou pourquoi un intervalle de healthcheck spécifique est utilisé font partie de la documentation.
  • Ansible. Playbooks, rôles, fichiers de variables — YAML partout. Le support des commentaires est activement utilisé pour expliquer les paramètres de tâches non évidents.

TOML gagne quand vous contrôlez le format :

  • Projets Rust. Cargo.toml est la référence absolue. Déclarations de dépendances, feature flags, profils de build — tout en TOML. Les types explicites signifient que les chaînes de version comme "1.0.0" restent des chaînes.
  • Projets Python. pyproject.toml est devenu le standard pour les métadonnées de projets Python, la configuration de build, et les paramètres d'outils (Black, isort, mypy, pytest lisent tous depuis ce fichier).
  • Configuration d'outils où l'ambiguïté cause des bugs. Si votre configuration contient des chaînes de version, des codes de pays, ou d'autres valeurs qui pourraient ressembler à des booléens ou des nombres, les types explicites de TOML éliminent toute une catégorie de surprises de parsage.
  • Configurations plates. Quand votre configuration est principalement des paires clé-valeur à un ou deux niveaux d'imbrication, TOML est plus lisible que YAML et plus propre que JSON.

Le guide de décision pratique

La plupart du temps, le choix est fait pour vous par l'écosystème. GitHub Actions, c'est YAML. Kubernetes, c'est YAML. Rust, c'est TOML. L'outillage Python, c'est TOML. Quand vous avez vraiment le choix, utilisez ce guide :

  • Utilisez YAML quand l'outillage l'impose — plateformes CI/CD, Kubernetes, charts Helm, Docker Compose, Ansible. Combattre la convention coûte plus que ça ne rapporte.
  • Utilisez YAML quand vous avez besoin d'ancres et d'alias pour garder une configuration complexe DRY — il n'y a pas d'équivalent TOML.
  • Utilisez TOML pour les manifestes de projets et la configuration d'outils que vous contrôlez — métadonnées de paquets, paramètres de linters, configuration de build.
  • Utilisez TOML quand votre configuration contient des valeurs que YAML 1.1 pourrait mal interpréter — chaînes de version, codes de pays, tout ce qui ressemble à un booléen ou un nombre.
  • Mettez vos chaînes YAML entre guillemets chaque fois que la valeur pourrait être confondue avec un booléen, un nombre ou null. Particulièrement : les numéros de version, les codes de pays, tout ce qui commence par un chiffre, et les valeurs comme yes, no, on, off.

Travailler avec les deux formats

Besoin de valider ou de mettre en forme un fichier de configuration ? Le Formateur TOML gère les fichiers TOML, et le Formateur YAML couvre YAML. Si vous devez migrer une configuration d'un format à l'autre — par exemple, convertir une configuration d'outil basée sur YAML en TOML pour un projet qui le préfère — les convertisseurs TOML vers JSON et YAML vers JSON produisent tous deux du JSON, que vous pouvez ensuite convertir vers votre format cible. Parfois, passer par JSON comme étape intermédiaire est le chemin le plus fiable.

Une chose à savoir : les deux formats ont leurs propres sous-ensembles compatibles JSON. Le JSON valide est du YAML valide (YAML est un sur-ensemble de JSON). TOML n'a pas cette relation avec JSON, mais la spécification TOML est intentionnellement simple — c'est une lecture courte, ce qu'on ne peut pas dire de la spécification YAML 1.2 complète.

En conclusion

TOML et YAML ne sont pas vraiment en compétition. Ils se sont installés dans des niches différentes basées sur leurs priorités de conception. Les types implicites et la structure basée sur l'indentation de YAML en font le choix naturel pour les grandes configurations imbriquées maintenues par des équipes — pensez aux manifestes Kubernetes et aux workflows GitHub Actions. Les types explicites et la structure de sections plates de TOML en font le choix naturel pour les manifestes de projets et la configuration d'outils où une chaîne de version ou un code pays mal lu causerait un vrai bug.

La seule chose à retenir : si vous écrivez du YAML et que vous avez des valeurs qui ressemblent à des booléens, des nombres ou des nulls — mettez-les entre guillemets. C'est la seule habitude qui prévient la plupart des bugs de configuration liés à YAML. Et si vous démarrez un nouveau projet et avez le choix de votre format de configuration, TOML vaut le coup d'œil. Le dépôt GitHub TOML contient de bons exemples, et une fois que vous avez écrit un Cargo.toml ou un pyproject.toml, l'attrait du format devient assez évident.