Plaidoyer pour YAML
(opensource.posit.co)- YAML 1.2 est un format de sérialisation basé sur l'indentation pour des fichiers de configuration imbriqués rédigés à la main, et il faut le distinguer des critiques sur son instabilité héritées de l'ancienne expérience PyYAML
- Les formats de fichiers de configuration ont évolué en corrigeant les limites de la génération précédente, comme la platitude de INI, la verbosité de XML, ou l'absence de commentaires et de chaînes multilignes dans JSON
- TOML est clair pour des structures peu profondes comme
pyproject.tomletCargo.toml, mais avec des structures imbriquées profondes, la répétition des chemins et les tableaux de tables alourdissent la lecture et l'édition - Le Core Schema de YAML 1.2 traite
yes,no,on,off,y,ncomme des chaînes, et supprime les nombres en base 60 ainsi que les horodatages comme type fondamental, ce qui réduit les anciens problèmes d'inférence implicite de type - py-yaml12 est un parseur/formateur YAML 1.2 pour Python qui, grâce à une implémentation en Rust, fournit des valeurs par défaut sûres, une conformité à 100 % à
yaml-test-suite, et des performances comparables à l'extension C de PyYAML
Constat
- Ces dernières années, l'opinion sur les formats de configuration a évolué vers l'idée que YAML est mauvais et TOML bon, mais évaluer YAML exige de considérer à la fois son histoire, l'évolution de sa spécification et l'état des outils en 2026
- Les critiques envers YAML ont longtemps été justifiées, et même des utilisateurs prudents ont rencontré des comportements inattendus, mais la spécification a changé et l'écosystème d'outils rattrape son retard
- Les critiques actuelles contre YAML ne peuvent se comprendre qu'en replaçant le sujet dans la généalogie des formats de configuration, où chaque nouveau format corrige les excès du précédent dans un débat récurrent
Brève histoire des formats de fichiers de configuration
- Les fichiers INI sont apparus au début des années 1980 avec MS-DOS et les premières versions de Windows ; c'est un format plat, lisible et éditable à la main, composé de paires clé-valeur, de sections entre crochets et de commentaires commençant par un point-virgule
- INI suffisait pour les besoins de l'époque, comme la configuration de pilotes, les chemins de polices ou les paramètres applicatifs, mais souffrait de limites structurelles : pas d'imbrication sur plus d'un niveau et aucune spécification officielle, ce qui a conduit chaque parseur à implémenter son propre dialecte
- XML a été largement adopté à la fin des années 1990 dans le monde des logiciels d'entreprise ; il apportait hiérarchie arbitraire, schémas, espaces de noms, transformations et structure auto-descriptive, mais en pratique les fichiers de configuration devenaient très verbeux et difficiles à maintenir à la main
- JSON est apparu comme une réaction plus légère à XML et, grâce à la simplicité des littéraux d'objet JavaScript, a remplacé XML dans de nombreuses API web à la fin des années 2000 et au début des années 2010 ; en tant que format de configuration, il reste toutefois limité par l'absence de commentaires, l'absence de chaînes multilignes et l'interdiction des virgules finales
- YAML en 2001 et TOML en 2013 sont apparus pour combler ce que JSON laissait de côté ; YAML propose une syntaxe basée sur l'indentation avec imbrication arbitraire, documents multiples, références et types personnalisés, tandis que TOML vise un « INI standardisé » avec typage explicite et spécification officielle
- Chaque nouvelle génération de formats est partie des excès de la précédente, et une grande partie des problèmes actuels reprochés à YAML vient moins de sa conception que d'une version particulière de sa spécification et des parseurs restés liés à cette version
Les problèmes qui fondaient l'opposition à YAML
- Les critiques envers YAML ne relèvent pas d'un procès artificiel : elles s'appuient sur des expériences bien réelles vécues par les programmeurs pendant des années
- Le plus célèbre est le problème dit de la Norvège : en YAML 1.1, le scalaire non cité
NOest interprété comme le booléenfalse, ce qui transformenodans une liste de codes pays en valeur faussecountries: - dk - fi - is - no - se["dk", "fi", "is", false, "se"] - La même règle s'appliquait aussi à
yes,on,off,y,net à diverses variantes de casse ; des valeurs comme22:22pour un mapping de ports pouvaient être lues comme un entier en base 60, des numéros de version comme10.23comme des flottants au lieu de chaînes, et des valeurs ressemblant à des dates comme des horodatages - Dans le code de data science et de machine learning, des noms de variables naturels comme
netypouvaient eux aussi, sous les règles de booléens implicites de YAML 1.1, être interprétés non comme des clés chaîne mais comme des clésFalseetTrue{"variables": {"x": "features", False: "sample_size", True: "target"}} - L'inférence implicite de type très agressive de YAML 1.1 visait à rendre plus lisible la lecture de
truenon cité comme booléen, mais dans de vrais fichiers de configuration elle produisait surtout de l'imprévisibilité - Les fichiers de configuration sont souvent modifiés rarement et par des personnes qui ne sont pas l'auteur initial ; dans ce contexte, un mauvais parsing silencieux qui se propage pendant des mois dans un système est particulièrement difficile à tolérer
- La complexité de la spécification YAML dans son ensemble constituait aussi un fardeau ; la spécification YAML 1.2.2 est un document conséquent, composé de 10 chapitres et d'une structure de sections numérotées sur quatre niveaux
- Les nombreuses façons d'exprimer des chaînes multilignes, ainsi que les ancres et alias, offrent un puissant système de références, mais représentent pour la plupart des usages de configuration une charge conceptuelle supérieure au nécessaire
- Les problèmes de sécurité liés à la désérialisation d'objets basée sur les tags, en particulier la vulnérabilité de
yaml.load()en Python, sont devenus un vecteur d'attaque bien connu, et ces critiques étaient bel et bien valables pour YAML 1.1 et l'écosystème d'outils qui l'entourait
Ce que TOML fait bien
- TOML est un format propre, lisible et sans ambiguïté pour des structures de configuration plates ou peu profondes
- Sa syntaxe est familière à quiconque a déjà vu des fichiers INI, tout en ajoutant des types explicites, une spécification officielle et la prise en charge de tables imbriquées au moyen de clés séparées par des points
pyproject.tomletCargo.tomlsont des cas où TOML fonctionne bien, car ils contiennent généralement des sections bien définies, d'une profondeur d'un ou deux niveaux, avec un contenu prévisible- Dans TOML, les chaînes sont toujours entre guillemets ; il n'y a donc aucune ambiguïté sur le fait que
nosoit un booléen ou un mot, les entiers, flottants et dates sont des types de premier ordre, et les commentaires fonctionnent comme on s'y attend - L'adoption de la PEP 518 dans l'écosystème de packaging Python et l'usage de Cargo dans la communauté Rust montrent que TOML est un choix adapté dans ce périmètre de problèmes
- La spécification TOML est assez courte pour qu'un développeur compétent puisse écrire un parseur compatible en un week-end, ce qui se traduit par un écosystème de parseurs large, bien testé et cohérent
- TOML ne connaît pas de fracture de versions comparable à celle entre YAML 1.1 et 1.2 ; TOML 1.0 signifie partout la même chose
Là où TOML devient lourd
- Quand un fichier de configuration doit exprimer de la profondeur, la structure imbriquée de TOML repose sur des en-têtes de section à points (
[servers.alpha]) ou des tableaux de tables ([[products]]), ce qui devient de plus en plus difficile à lire à mesure que l'imbrication augmente - Martin Vejnár, de PyTOML, a refusé que sa bibliothèque devienne une dépendance de
pip, en expliquant notamment que lorsque les schémas de configuration deviennent complexes, la syntaxe TOML devient peu élégante et difficile à lire - En YAML, l'indentation rend immédiatement visible la hiérarchie, alors qu'en TOML il faut répéter tout le chemin dans chaque en-tête de section
services: web: image: nginx:latest environment: DB_HOST: postgres DB_PORT: 5432 resources: limits: memory: 512M cpu: "0.5"[services.web] image = "nginx:latest" [services.web.environment] DB_HOST = "postgres" DB_PORT = 5432 [services.web.resources.limits] memory = "512M" cpu = "0.5" - Le lecteur de TOML doit reconstruire mentalement une structure arborescente à partir d'une succession de noms qualifiés plats, et selon les mesures de la documentation de StrictYAML, un fichier TOML utilise environ 50 % de caractères en plus pour exprimer les mêmes données à cause de la répétition des préfixes de chemin
- Python a montré depuis longtemps que l'indentation comme structure n'est pas une faiblesse mais une force, en éliminant une classe de bugs où structure visuelle et structure syntaxique divergent
- YAML hérite de cet avantage lié à l'indentation, alors que TOML, qui ne l'exige pas, ne matérialise la relation entre les clés et les tables contenantes que dans les en-têtes de section, et non dans la disposition physique du fichier
- Dans des fichiers de configuration profondément imbriqués, TOML devient difficile à parcourir et difficile à éditer avec assurance
Les changements de YAML 1.2
- La spécification YAML 1.2 a été publiée en 2009, et sa révision de clarification 1.2.2 a été finalisée en octobre 2021
- Dans le Core Schema de YAML 1.2, seuls
true,falseainsi queTrue,False,TRUE,FALSEsont reconnus comme booléens ;yes,no,on,off,y,nsont des chaînes ordinaires - Les littéraux numériques en base 60 à l'origine du problème
22:22ont été entièrement supprimés, et les horodatages ne sont plus un type fondamental ; dans le Core Schema, un2026-05-05non cité est donc une chaîne, et non une date détectée automatiquement - JSON est devenu un sous-ensemble strict et correct de YAML 1.2, et tout document JSON valide est analysé de la même manière en YAML
- Les règles d'interprétation des tags sont plus strictes et plus claires, et la spécification elle-même est rédigée de manière plus explicite et maintenue publiquement sur GitHub
- Le YAML que les gens critiquaient est YAML 1.1 ; la spécification qui domine aujourd'hui est le YAML 1.2, plus sûr et plus prévisible
- Le problème est que l'expérience des utilisateurs avec YAML est médiatisée non par la spécification, mais par les parseurs ; pour la plupart des utilisateurs Python, ce parseur est PyYAML, qui implémente YAML 1.1 et n'a pas changé sa sémantique de base depuis 2006
Le paysage des parseurs YAML en Python
- PyYAML est la bibliothèque YAML de référence de facto en Python ; elle encapsule la bibliothèque C LibYAML pour les performances et propose aussi une alternative en Python pur
- PyYAML est téléchargée des millions de fois chaque semaine et sert de dépendance à d'innombrables packages, mais elle implémente YAML 1.1 ; c'est la racine de l'expérience « YAML a analysé un code pays comme un booléen » dans l'écosystème Python
- Le dépôt PyYAML compte plus de 200 issues ouvertes et plus de 100 pull requests ouvertes ; le projet est maintenu, mais avance lentement, et la transition de version majeure vers la sémantique YAML 1.2 n'a toujours pas eu lieu
- ruamel.yaml prend en charge YAML 1.2 et offre des capacités d'édition en aller-retour qui préservent les commentaires, le flow style et l'ordre des clés ; c'est une option bien plus puissante que PyYAML pour préserver les commentaires ou faire des éditions sensibles au formatage
- ruamel.yaml est toutefois nettement plus lent que le chemin rapide C de PyYAML, car son mode d'aller-retour par défaut repose surtout sur une implémentation en Python pur, et son historique de packaging a parfois compliqué les pipelines de déploiement à cause de problèmes de namespace package et de chaîne de dépendances
- StrictYAML implémente un sous-ensemble volontaire de YAML, supprimant toute inférence implicite de type, les tags, les ancres et le flow style
- Philosophquement, StrictYAML est plus proche de TOML que de YAML complet ; c'est un format sûr et simple utilisant la syntaxe indentée de YAML, mais limité à Python, sans implémentation dans d'autres langages, et sans ambition de conformité à la spécification
- Dans ce paysage, il manquait une bibliothèque à la fois rapide, pleinement conforme à YAML 1.2 et facile à substituer à l'interface de base de PyYAML
Présentation de py-yaml12
- py-yaml12 est un parseur/formateur YAML 1.2 pour Python, implémenté en Rust pour la vitesse et la précision
- py-yaml12 est construit au-dessus de la crate YAML Rust
saphyret propose une API petite et ciblée :parse_yaml(),read_yaml()pour le chargement,format_yaml(),write_yaml()pour la sérialisation -
Simplicité
- Pour la plupart des usages, la conception permet de travailler de bout en bout uniquement avec les types natifs Python de base comme
dict,list,int,float,str,None - Il n'y a ni classe de document spéciale ni type de nœud personnalisé sur le chemin commun, et comme YAML 1.2 est un sur-ensemble de JSON, tout JSON valide est analysé de manière identique
- py-yaml12 atteint 100 % de conformité sur
yaml-test-suite, la collection communautaire de cas limites et de tests de conformité - Dans une liste
regions,nodevient silencieusementFalseavec le comportement YAML 1.1 de PyYAML, alors qu'avec le comportement YAML 1.2 de py-yaml12, il reste la chaîne"no"comme l'exige la spécification - L'API de fichiers est elle aussi directe : si l'on écrit un dictionnaire Python sur disque puis qu'on le relit, on retrouve le même objet, sans perte au cours de l'aller-retour
- Pour les fonctionnalités YAML avancées comme les valeurs taguées, une enveloppe
Yamlest fournie, mais elle reste optionnelle pour les tâches de configuration courantes
- Pour la plupart des usages, la conception permet de travailler de bout en bout uniquement avec les types natifs Python de base comme
-
Sécurité
- Les valeurs par défaut de py-yaml12 sont pensées non seulement pour la commodité, mais aussi pour la sécurité ; l'approche inverse de PyYAML traite les tags comme des commandes, au point qu'un simple chargement d'un fichier YAML peut exécuter du code Python arbitraire
- Si l'on lit avec
yaml.load(f, Loader=yaml.Loader)un fichier YAML qui alias l'espace de noms des tags Python object-apply de PyYAML, le code Python est exécuté avant même que la fonction ne retourne un dictionnaire banal - Dans l'exemple, le résultat du parsing semble être
{'debug': False, 'retries': 3}, mais auparavant la variable d'environnementYAML_PAYLOAD_RANa été définie à'1', si bien qu'en ne regardant que le résultat on ne peut pas savoir que du code a été exécuté - py-yaml12 n'exécute pas les tags qui n'ont pas été explicitement choisis et les conserve comme données ; les tags non traités sont renvoyés dans une enveloppe
Yamlcontenant la valeur et le tag
-
Vitesse
- Les benchmarks de py-yaml12 comparent les performances en lecture et en écriture, sur des tailles de fichiers allant du kilooctet au mégaoctet, au chemin Python pur par défaut de PyYAML, à
CSafeLoaderetCSafeDumperbasés sur LibYAML, ainsi qu'à ruamel.yaml - Comme la logique centrale de parsing et de formatage est implémentée en Rust compilé plutôt qu'en Python interprété, py-yaml12 offre des performances comparables à l'extension C de PyYAML tout en conservant une conformité complète à YAML 1.2
- Il existe encore peu d'options dans l'écosystème Python qui réunissent à la fois une conformité complète à YAML 1.2 et des performances du niveau de l'extension C de PyYAML
- Les benchmarks de py-yaml12 comparent les performances en lecture et en écriture, sur des tailles de fichiers allant du kilooctet au mégaoctet, au chemin Python pur par défaut de PyYAML, à
Conclusion
- Le débat habituel YAML contre TOML ressemble désormais davantage à un débat contre une forme de YAML problématique qui n'existe plus vraiment ; les critiques étaient réelles, mais relèvent en grande partie de l'histoire
- Les critiques contre YAML visent souvent non pas la spécification ni un YAML 1.2 correctement implémenté, mais le YAML 1.1 tel qu'il a été vécu à travers PyYAML
- TOML reste un bon choix pour des configurations peu profondes et plates, et
pyproject.tomlcorrespond bien à ce rôle, mais l'affirmation selon laquelle YAML serait intrinsèquement non sûr ou imprévisible ne tient plus face à un parseur YAML 1.2 conforme - La vraie question n'est pas de savoir abstraitement quel format est le meilleur, mais quel format et quels outils conviennent à une tâche donnée
- Pour des fichiers de configuration complexes, imbriqués et rédigés par des humains, YAML 1.2 avec un parseur moderne constitue une réponse solide ; les formats s'améliorent en laissant chaque génération corriger les aspérités de la précédente
- En Python,
pip install py-yaml12permet de découvrir une expérience YAML moderne et conforme à la spécification - Dans l'environnement R, r-yaml12 offre les mêmes avantages, avec une conformité complète à YAML 1.2, des performances fondées sur Rust et des valeurs par défaut sûres, au même titre que le package Python
1 commentaires
Avis sur Lobste.rs
Expliquer que YAML est apparu pour combler les lacunes de JSON paraît étrange. De mémoire, YAML vient de la communauté Perl, et il est possible qu’il soit contemporain de JSON, voire plus ancien.
En vérifiant l’histoire réelle, on trouve un texte intéressant d’Ingy dot Net, l’un des créateurs de YAML, qui situe ses origines entre novembre 1999 et janvier 2001.
Et d’après cet article sur l’histoire de JSON, une forme primitive de JSON est apparue en avril 2001, lorsqu’on envoyait des messages du serveur vers le client au moyen d’iframes cachées et de balises
<script>, et ce n’est qu’en 2002, lorsque Douglas Crockford a publié json.org, qu’il est devenu un format autonome.Donc YAML a légèrement précédé JSON, ou, au mieux, les deux se sont développés en parallèle. Dire que YAML serait une réaction à JSON ou qu’il aurait été créé pour combler ses manques n’est pas exact : YAML était en réalité une réaction à XML. JSON aussi est parti d’une raison pratique — injecter directement des données dans une balise
<script>— mais il est difficile de nier qu’il a gagné en popularité en s’imposant comme alternative plus simple à XML.Le texte lui-même donne aussi l’impression d’avoir été écrit par un LLM, et le projet présenté semble lui aussi relever de la vibe coding.
Chaque phrase prise séparément paraît plausible, mais l’ensemble est déroutant. Et défendre TOML comme un langage doté d’une spécification tout en critiquant YAML pour avoir une spécification complexe paraît aussi un peu étrange. On finirait presque par se demander si YAML n’avait pas de spécification à l’origine alors que TOML, lui, en avait une.
JSON est en fait assez proche de Data-E façon ECMAScript. La page archivée de Data-E montre d’ailleurs qu’eux aussi réagissaient à XML.
En utilisant les tables inline, l’exemple de “mauvais” TOML devient ceci :
Ça reste tout à fait correct visuellement et, si on compte les caractères, cela semble même plus court que l’exemple YAML.
Comme l’objectif de l’article est de défendre YAML, c’est peut-être hors sujet, mais j’aurais aimé qu’il parle aussi de TOML 1.1 et des nouvelles fonctionnalités des tables inline. Cela corrige trois choses que je n’aimais pas dans JSON : les noms de clés sans guillemets, les chaînes de commentaire et les virgules finales.
Python 3.15 prend en charge TOML 1.1, et l’adoption de TOML 1.1 semble bien meilleure que celle de YAML 1.2. Mettre à jour un parseur TOML 1.0 vers 1.1 sera probablement presque trivial, alors que faire passer YAML 1.1 à 1.2 ne le sera sans doute pas. Je trouve d’ailleurs mal la liste des changements sur yaml.org ; je n’y vois surtout que deux énormes spécifications.
Des choses comme le “Norway Problem” ne sont qu’une petite note de bas de page parmi les raisons pour lesquelles je n’aime pas YAML ; ce que je lui reproche vraiment, c’est d’être pénible à éditer, d’utiliser des espaces significatifs et, globalement, d’être assez complexe.
À mes yeux, YAML, JSON et TOML ont chacun leur domaine. L’espace qui m’a longtemps semblé vide a été comblé par https://kdl.dev/
JSON = échange de données arbitrairement imbriquées
YAML = format conteneur peu profond pour contenir des chaînes multilignes
TOML = fichier de configuration presque plat
KDL = représentation de données d’un DSL de style Ruby
Je n’aime pas YAML à cause de choses comme le Norway problem. YAML 1.2 l’a un peu atténué, mais à cause des chaînes non citées, des chaînes comme
"","Null","true","FALSE"posent encore problème, et il faut un encodeur qui connaît YAML. Je n’aime pas non plus sa complexité générale, mais la vraie raison pour laquelle je déteste YAML, c’est çaPEP-8 recommande lui aussi fortement de ne pas utiliser de tabulations pour l’indentation
Je ne pense pas que l’argument « ce n’est pas YAML qui était mauvais, c’était YAML 1.1, mais la plupart des parseurs sont encore en 1.1 » fonctionne aussi bien que l’article le voudrait
J’aime bien YAML quand il est utilisé avec retenue, et je me réjouis aussi de voir apparaître de nouveaux parseurs hautes performances pour YAML 1.2, mais si la version “mauvaise” reste encore celle qui est majoritairement utilisée, je choisirai probablement autre chose. Si je ne peux pas contrôler quel parseur sera utilisé et donc avoir confiance dans le fait que mon YAML sera interprété correctement, alors YAML dans son ensemble reste encore “mauvais”
Tout le monde devrait migrer vers la 1.2, mais en attendant, je pense qu’il est raisonnable de traiter YAML avec prudence
Devant la phrase « le débat YAML contre TOML consiste généralement à attaquer un format qui n’existe plus vraiment sous sa forme problématique, et les plaintes sont réelles mais historiques », j’ai envie de pousser un cri en pensant à GitHub Actions et Kubernetes
Les arguments de défense sont solides. Malgré tout, pour des documents très simples, TOML est plus lisible, et il est plus facile d’amener les gens à utiliser TOML plutôt que YAML
Malheureusement, il y a une très longue inertie dans la manière dont la perception publique des développeurs évolue vis-à-vis des outils. Les gens lisent un certain récit, se font une opinion, puis passent à un autre outil qui n’a pas commis d’erreur publique
J’aimerais que le parseur YAML inclus dans l’interpréteur Ruby prenne en charge YAML 1.2.2
Mais je ne vois pas bien comment basculer la version par défaut sans casser l’écosystème
J’aimerais que les formats de fichiers de configuration puissent spécifier un schéma standardisé. Ainsi, un éditeur pourrait examiner n’importe quel fichier de configuration et signaler les clés mal orthographiées ou les incompatibilités de type
Il devrait aussi pouvoir documenter le rôle des clés via des indices au survol, et fournir facilement l’autocomplétion des clés valides. Ce serait encore mieux s’il prenait en charge de simples assertions ou contrats pour détecter les valeurs incorrectes. Par exemple, une clé
"color"devrait correspondre à/#[a-fA-F0-9]{6}/Idéalement, cela devrait aussi permettre de générer automatiquement les structures de données pour les fichiers de configuration
Il existe plusieurs formats de spécification de validation comme
XSDouRelax NG, et je suis surtout familier avec les XML DTD, donc j’aurais du mal à parler des autres$schemaà la racine qui référence un fichier JSON Schema définissant le bon schéma. C’est essentiellement du XSD pour JSON. Les bons éditeurs peuvent généralement s’en servir pour fournir de l’autocomplétion et des soulignements rougesL’avantage, c’est que TOML et YAML sont à peu près du JSON avec une syntaxe différente, donc on peut souvent aussi utiliser des fichiers JSON Schema avec eux