1 ポイント 投稿者 GN⁺ 21 시간 전 | 1件のコメント | WhatsAppで共有
  • YAML 1.2は、人が直接書くネストした設定ファイルのためのインデントベースのシリアライズ形式であり、古いPyYAML体験に由来する不安定性への批判とは切り分けて考える必要がある
  • 設定ファイル形式は、INIの平坦さ、XMLの冗長さ、JSONのコメント・複数行文字列の欠如といった前世代の限界を補う流れの中で変化してきた
  • TOMLpyproject.tomlCargo.tomlのような浅い構造では明快だが、深いネスト構造ではパスの繰り返しや配列テーブルのため、読み書き・編集の負担が増える
  • YAML 1.2 Core Schemayes, no, on, off, y, nを文字列として扱い、60進数の数値とコア型としてのタイムスタンプを削除することで、過去の暗黙的な型推論の問題を縮小した
  • py-yaml12はPython向けのYAML 1.2パーサー兼フォーマッタで、Rust実装により安全なデフォルト、yaml-test-suite 100%準拠、PyYAMLのC拡張と競合できる性能を提供する

問題意識

  • ここ数年で設定ファイル形式をめぐる空気は「YAMLは悪く、TOMLは良い」という方向に傾いてきたが、YAMLの評価は歴史、仕様の変化、2026年時点のツール状況をあわせて見るべき問題である
  • YAML批判は長いあいだ妥当であり、慎重な利用者でさえ予想外の挙動に遭遇してきたが、仕様は変わり、ツールのエコシステムも追いつきつつある
  • 現在のYAML批判は、設定ファイル形式の系譜をあわせて見ないと理解しにくく、前の形式の行き過ぎを次の形式が正すという議論が繰り返されてきた流れの一部である

設定ファイル形式の簡単な歴史

  • INIファイルは1980年代初頭にMS-DOSや初期Windowsとともに登場し、キーと値の組、角括弧のセクション、セミコロンのコメントを持つ、平坦で読みやすく人が編集しやすい形式だった
  • INIはデバイスドライバー設定、フォントパス指定、アプリケーション設定といった当時の要求には十分だったが、1段階以上のネストができず、正式な仕様もないため、パーサーごとに方言を実装するという構造的な限界があった
  • XMLは1990年代後半にエンタープライズソフトウェアの世界で広く採用され、任意の階層、スキーマ、名前空間、変換、自己記述的な構造を提供したが、実際の設定ファイルは非常に冗長になり、手作業で保守しにくい形式だった
  • JSONはXMLへの軽量な反動として登場し、JavaScriptオブジェクトリテラルの単純さを土台に2000年代後半から2010年代初頭のWeb APIでXMLを置き換えたが、設定ファイルとして使うにはコメントがなく、複数行文字列もなく、末尾カンマも禁止という制約があった
  • YAMLは2001年、TOMLは2013年にJSONが残した隙間を埋めるために登場し、YAMLはインデントベースの構文で任意のネスト、複数ドキュメント、参照、ユーザー定義型を提供し、TOMLは明示的な型と正式な仕様を備えた「標準化されたINI」を志向した
  • 各世代の新しい形式は前世代の過剰さを出発点としており、現在のYAMLの問題のかなりの部分は、形式設計そのものよりも特定の仕様バージョンと、それに縛られたパーサーの産物である

YAML反対の根拠だった問題点

  • YAML批判は作られた問題ではなく、長年にわたり実際のプログラマーが経験してきたことに基づくものだった
  • もっとも有名なNorway問題は、YAML 1.1では引用符のないスカラーNOが真偽値falseと解釈され、国コード一覧のnoが偽値に変わってしまう挙動である
    countries:
      - dk
      - fi
      - is
      - no
      - se
    
    ["dk", "fi", "is", false, "se"]
    
  • 同じ規則はyes, on, off, y, nやその大小文字の各種変形にも適用され、22:22のようなポートマッピングは60進数の整数に、10.23のようなバージョン番号は文字列ではなく浮動小数点数に、日付のように見える値はタイムスタンプに解釈される問題もあった
  • データサイエンスや機械学習のコードで自然な変数名であるnyも、YAML 1.1の暗黙的な真偽値規則の下では文字列キーではなくFalseTrueキーとして解釈されうる構造だった
    {"variables": {"x": "features", False: "sample_size", True: "target"}}
    
  • YAML 1.1の攻撃的な暗黙的型推論は、引用符のないtrueを真偽値として読んで可読性を高める意図があったが、実際の設定ファイルでは予測不可能性を大きくする結果になった
  • 設定ファイルは頻繁に編集されず、元の作者ではない人が修正することも多いため、静かな誤解析が何か月もかけてシステム全体に広がりうるという点で、予想外の挙動に最も弱い領域である
  • YAML仕様全体の複雑さも負担となり、YAML 1.2.2仕様は10章と4段階の番号付きセクション構造を持つかなり大きな文書である
  • 複数行文字列の表現方法が多く、アンカーとエイリアスは強力な参照システムを作るが、多くの設定作業には必要以上の概念的重さがある
  • タグベースのオブジェクト逆シリアライズのセキュリティ問題、特にPythonのyaml.load()の脆弱性はよく知られた攻撃ベクトルとなり、こうした批判はYAML 1.1とその周辺ツールのエコシステムに対しては妥当だった

TOMLが得意な点

  • TOMLは平坦または浅い設定構造では、整っていて読みやすく曖昧さのない形式である
  • TOMLの構文はINIファイルを見たことがある人にはなじみやすく、それでいて明示的な型、正式な仕様、ドット区切りキーによるネストテーブル対応が追加されている
  • pyproject.tomlCargo.tomlは通常1〜2段階程度の深さで、よく定義されたセクションと予測可能な内容を持つため、TOMLがうまく合う事例である
  • TOMLでは文字列は常に引用符で囲まれるため、noが真偽値なのか単語なのか曖昧ではなく、整数・浮動小数点数・日付が第一級の型であり、コメントも期待どおりに機能する
  • PythonパッケージングエコシステムでのPEP 518採用や、RustコミュニティでのCargo利用は、この問題領域においてTOMLが適切な選択である根拠になっている
  • TOML仕様は、有能なプログラマーなら週末で互換パーサーを書ける程度に短く、その結果としてパーサーのエコシステムが大きく、十分にテストされ、一貫しているという利点がある
  • TOMLにはYAML 1.1と1.2のあいだのバージョン分裂に相当する問題がなく、TOML 1.0はどこでもTOML 1.0であるという一貫性がある

TOMLが苦しくなる場面

  • 設定ファイルが深さを表現する必要がある場合、TOMLのネスト構造はドット区切りのセクションヘッダー([servers.alpha])や配列テーブル([[products]])に依存し、ネストが増えるほど読みにくくなる
  • PyTOMLのMartin Vejnárは、自身のライブラリがpipの依存関係になることを拒否した際、設定スキーマが複雑になるとTOML構文は見栄えが悪く読みづらいという経験を理由に挙げた
  • YAMLではインデントが階層をひと目で伝えるが、TOMLでは各セクションヘッダーに完全なパスを繰り返さなければならないという違いがある
    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"
    
  • TOMLの読者は平坦な修飾名の連続からツリー構造を頭の中で再構成しなければならず、StrictYAML文書の計測では、同じデータを表現する際にTOMLファイルは繰り返されるパス接頭辞のため約50%多くの文字数を使う
  • Pythonは、インデントを構造として使う方法が弱点ではなく強みであることをはるか以前に示しており、視覚的構造と構文構造のずれから生じる種類のバグを取り除く効果がある
  • YAMLはこのインデント構造の利点を受け継いでいるが、TOMLはインデントを要求しないため、キーと包含テーブルの関係はファイルの物理的配置ではなくセクションヘッダーの中にしか存在しない
  • 深くネストした設定ファイルでは、TOMLは見渡しづらく、自信を持って編集しにくい形式になる

YAML 1.2の変更点

  • YAML 1.2仕様は2009年に公開され、明確化改訂版である1.2.2は2021年10月に完了した
  • YAML 1.2 Core Schemaでは、true, falseTrue, False, TRUE, FALSEだけが真偽値として認識され、yes, no, on, off, y, nは通常の文字列である
  • 22:22問題を生んだ60進数リテラルは完全に削除され、タイムスタンプはもはやコア型ではないため、Core Schemaでは引用符なしの2026-05-05は自動検出される日付ではなく文字列になる
  • JSONはYAML 1.2の厳密で正しい部分集合となり、有効なJSON文書はYAMLとしても同じようにパースされる
  • タグ解釈規則はより厳格かつ明確になり、仕様自体もより明瞭に書かれ、GitHubで公開メンテナンスされている
  • 人々が批判してきたYAMLはYAML 1.1であり、現在言語を支配している仕様は、より安全で予測可能なYAML 1.2である
  • 問題は、ユーザーのYAML体験が仕様ではなくパーサーを通して媒介される点であり、Python利用者の大半にとってそのパーサーは、YAML 1.1を実装し、2006年以降コアの意味論を変えていないPyYAMLである

Python YAMLパーサーの地形

  • PyYAMLはPythonにおける事実上の標準YAMLライブラリであり、性能のためにCライブラリLibYAMLをラップし、純粋Pythonの代替経路も提供している
  • PyYAMLは毎週数百万回ダウンロードされ、無数のパッケージの依存関係になっているが、YAML 1.1を実装しており、Pythonエコシステムで「YAMLが国コードを真偽値としてパースした」という体験の根源である
  • PyYAMLリポジトリには200件を超える未解決issueと100件を超える未マージpull requestがあり、プロジェクトは保守されているものの動きは遅く、YAML 1.2セマンティクスへのメジャーバージョン移行は実現していない
  • ruamel.yamlはYAML 1.2対応に加え、コメント、flow style、キー順序を保持するラウンドトリップ編集機能を提供し、コメント保持や書式認識編集ではPyYAMLよりはるかに強力な選択肢である
  • ruamel.yamlはデフォルトのラウンドトリップモードでは主に純粋Python実装であるため、PyYAMLのCベース高速経路よりかなり遅く、名前空間パッケージ問題や依存関係チェーンのため配布パイプラインを混乱させたパッケージング履歴もある
  • StrictYAMLはYAMLの意図的な部分集合を実装し、あらゆる暗黙的型推論、タグ、アンカー、flow styleを取り除いた形式である
  • StrictYAMLは思想的には完全なYAMLよりTOMLに近く、YAMLのインデント構文を使う安全で単純な形式だが、Python専用であり、他言語実装もなく、仕様準拠も目標としていない
  • この状況では、高速で完全なYAML 1.2準拠を提供し、PyYAMLの基本インターフェースを置き換えやすいライブラリが不足していた

py-yaml12の紹介

  • py-yaml12はPython向けのYAML 1.2パーサー兼フォーマッタであり、速度と正確性のためRustで実装されたライブラリである
  • py-yaml12はRustのYAMLライブラリsaphyrクレート上に構築され、読み込み用のparse_yaml(), read_yaml(), シリアライズ用のformat_yaml(), write_yaml()という小さく焦点の定まったAPIを提供する
  • 単純性

    • 大半のユースケースでは、dict, list, int, float, str, Noneのような基本的なPython組み込み型だけで最初から最後まで扱える設計
    • 通常経路には特別なドキュメントクラスやユーザー定義ノード型はなく、YAML 1.2はJSONの上位集合なので、すべての有効なJSONは同じようにパースされる
    • py-yaml12は、コミュニティ保守のエッジケースと適合性テスト集であるyaml-test-suiteで100%準拠を達成している
    • regionsリストのnoは、PyYAMLのYAML 1.1挙動では静かにFalseになるが、py-yaml12のYAML 1.2挙動では仕様どおり文字列"no"のまま保たれる
    • ファイルAPIも直接的で、Python辞書をディスクに書いて再び読み込めば同じオブジェクトが返る、ロスレスなラウンドトリップ動作をする
    • タグ付き値のような高度なYAML機能にはYamlラッパー型を提供するが、一般的な設定作業では任意である
  • 安全性

    • py-yaml12のデフォルトは使いやすさだけでなく安全性も高く、PyYAMLの逆のアプローチはタグをコマンドのように扱うため、YAMLファイルを読むだけで任意のPythonコードを実行できる危険がある
    • PyYAMLのPython object-applyタグ名前空間をエイリアスにしたYAMLファイルをyaml.load(f, Loader=yaml.Loader)で読むと、普通の辞書を返す前にPythonコードが先に実行される構造である
    • 例ではパース結果が{'debug': False, 'retries': 3}に見えるが、その前に環境変数YAML_PAYLOAD_RAN'1'に設定されており、結果だけ見ても実行されたことがわからないという問題がある
    • py-yaml12は、明示的に選ばれていないタグを実行せずデータとして保持し、未処理タグは値とタグを含むYamlラッパーとして返す
  • 速度

    • py-yaml12のベンチマークは、キロバイトからメガバイトまでのファイルサイズで、読み書き性能をPyYAMLのデフォルト純粋Python経路、LibYAMLベースのCSafeLoaderCSafeDumper、ruamel.yamlと比較している
    • コアのパース・整形ロジックをインタプリタPythonではなくコンパイル済みRustで実装しているため、py-yaml12は完全なYAML 1.2準拠を維持しつつ、PyYAMLのC拡張と競合できる性能を持つ
    • 現在のPythonライブラリで、完全なYAML 1.2準拠とPyYAML C拡張級の性能を同時に提供する選択肢は多くない

結論

  • 一般的なYAML対TOMLの論争は、問題のあった姿のままではもはや存在しない形式を相手にした議論に近く、批判は現実のものだったが歴史的な性格を持つ
  • YAML批判は、仕様や正しく実装されたYAML 1.2ではなく、PyYAMLを通じて経験されたYAML 1.1を指している場合が多い
  • TOMLは浅く平坦な設定には依然として良い選択であり、pyproject.tomlもその役割に適しているが、YAMLが本質的に安全でない、あるいは予測不可能だという主張は、互換性のあるYAML 1.2パーサーの前では成り立たない
  • 重要なのは、抽象的にどの形式が最良かではなく、どの形式とどのツールが特定の作業に適しているかという問いである
  • 複雑でネストした、人が書く設定ファイルには、最新パーサーを備えたYAML 1.2が有力な答えであり、形式は前世代の粗い部分を次世代が正す形で改善されていく
  • Pythonではpip install py-yaml12で、現代的で仕様準拠のYAML体験を試せる
  • R環境ではr-yaml12が同じ利点を提供し、完全なYAML 1.2準拠、Rustベースの性能、安全なデフォルトをPythonパッケージと同様に提供する

1件のコメント

 
Lobste.rs の意見
  • YAML が JSON の隙間を埋めるために登場したという説明は妙だ。記憶では YAML は Perl コミュニティから出てきたもので、JSON と同時期か、むしろそれより古い可能性がある
    実際の歴史を調べると、YAML の創始者の一人である Ingy dot Net の興味深い記事では、その起源を 1999年11月〜2001年1月の間としている
    また、JSON の歴史に関する記事を見ると、原始的な JSON 形式は 2001年4月に隠し iframe と <script> タグでサーバからクライアントへメッセージを送る方式の中で登場し、Douglas Crockford が json.org を公開した 2002年になってようやく独立した形式になった
    したがって、YAML は JSON より少し先行していたか、甘めに見ても両者はほぼ同時に発展したと見るのが近い。JSON への反応として、あるいは JSON の欠点を埋めるために生まれたという説明は正確ではなく、YAML は実際には XML への反応だった。JSON も <script> タグにデータをそのまま入れるという実用的な理由から始まったが、XML より単純な代替として定着し人気を得たのは否定しがたい
    記事自体にも LLM が書いたような痕跡が見え、紹介されているプロジェクトもバイブコーディングっぽい

    • YAML がもともと XML への反応であり代替だったというのはその通り
    • YAML の複雑さを批判しておきながら、後で新しい YAML 仕様を称賛するくだりでは、自分も LLM っぽさを感じた。1.2.2 仕様は 4 階層目まで読んでも長いと批判しつつ、後段では 1.2.2 は依然として大きいが 1.1 よりはずっと複雑でないとしている
      文ごとにはもっともらしいが、全体としては混乱する。また、TOML は仕様のある言語だと擁護しながら、YAML は複雑な仕様があると批判するのも少し変だ。まるで YAML にはもともと仕様がなく、TOML にはあったかのようにも見える
    • その歴史にもう一つ付け加えるなら、Crockford はもともと E の部分集合である Data-E を扱っており、Data-E は単純なデータだけを表現していた。E の作者たちは自前の言語を推すより、ECMAScript を capability-safe にする方向へ転換し、その結果 WeakMap のような E のさまざまなアイデアが ECMAScript に取り込まれた
      JSON は実際のところ ECMAScript 風の Data-E に近い。Data-E のアーカイブページを見ると、彼らもまた XML への反応として動いていたことが分かる
    • この記事を善意で読み直すなら、YAML の開発そのものではなく、YAML の採用が JSON の限界への反応だったとは言えるかもしれない。個人的にも JSON がすでに広く普及した後になって初めて YAML を知った。ただし、この認識を裏付けるデータはない
  • インラインテーブルを使えば、「悪い」TOML の例はこうなる

    [services.web]  
    image = "nginx:latest"
    
    environment = {  
      DB_HOST = "postgres",  
      DB_PORT = 5432,  
    }
    
    resources.limits = {  
      memory = "512M",  
      cpu = "0.5",  
    }  
    

    見た目は悪くないし、文字数だけで比べるなら YAML の例よりさらに短く見える

  • この記事の目的が YAML の擁護である点を考えると範囲外かもしれないが、TOML 1.1 と新しいインラインテーブル機能にも触れてほしかった。JSON で嫌だった 3 つの点を解決してくれる。引用符なしのキー名、コメント文字列、末尾カンマをサポートしている

    • 「YAML への批判は、今では問題のある形では存在しないフォーマットを攻撃している」と言うのは一理あるが、そのあとで 古い TOML バージョンを相手に反論するのは少し妙だ
      Python 3.15 は TOML 1.1 をサポートしており、TOML 1.1 の採用状況は YAML 1.2 よりはるかに良さそうに見える。TOML 1.0 パーサを 1.1 に更新するのはほぼ些細な作業で済む可能性が高いが、YAML 1.1 を 1.2 に上げるのはそうはいかなさそうだ。yaml.org でも変更点の一覧をうまく見つけられず、巨大な仕様書が 2 つ見えるだけだった
      「Norway Problem」のような話は、自分が YAML を嫌う理由の中では小さな脚注に近く、本当に嫌なのは編集しづらく、意味のある空白を使い、全体としてかなり複雑なところだ
  • YAML、JSON、TOML にはそれぞれ自分の領域があると思う。長いこと空いていると感じていた場所は https://kdl.dev/ が埋めてくれた
    JSON = 任意にネストできるデータ交換
    YAML = 複数行文字列を入れるための浅いコンテナ形式
    TOML = ほぼフラットな設定ファイル
    KDL = Ruby スタイルの DSL をデータとして表現

    • 新しいプロジェクトで設定が必要なら、自分も KDL に落ち着いた。無関係な区切り記法やインデント規則を多く取り除いてくれるからだ
    • KDL は本当に良い。使う機会をずっと探してしまう。XML のように属性と子ノードを同時に持つとマークアップがずっと読みやすくなる場面があり、そういう選択肢を 軽量な文法で提供してくれるのが良い
    • 例を見た瞬間に「これ、SDLang みたいだな」と思ったが、偶然ではなかった。KDL を知れてよかった
  • YAMLは Norway problem のようなものがあるので嫌い。YAML 1.2で多少は軽減されたが、引用符なし文字列のせいで "", "Null", "true", "FALSE" のような文字列も依然として問題になり、YAMLを理解するエンコーダが必要になる。全体的な複雑さも嫌だが、私が YAML を憎む本当の理由はこれ

    tab characters must not be used in indentation
    インデントに意味がある場合、タブとスペースを混在させたり一貫性なく使ったりするのを禁じるのは構わない。Python 3 のアプローチはかなり良さそうに見える
    Indentation is rejected as inconsistent if a source file mixes tabs and spaces in a way that makes the meaning dependent on the worth of a tab in spaces; a TabError is raised in that case.
    しかし YAML は、インデントを許可または要求しながら、タブとスペースの両方をサポートしない唯一のファイル形式のように見える
    Make はスペースをサポートしないと言えるかもしれないが、.RECIPEPREFIXをタブ以外の値に設定できる。そもそも Make におけるタブは、インデントというより Make が使う印だと見ることもできる

    • 今は見つけられないが、Guido van Rossum が Python を最初から作り直すなら必ず変えたいことの一つとして、インデントで実際のタブ文字を禁止することを挙げていた、という引用を読んだ記憶がある
      PEP-8 でもインデントにタブを使わないよう強く推奨している

      Tabs should be used solely to remain consistent with code that is already indented with tabs.
      -- https://peps.python.org/pep-0008/#tabs-or-spaces

    • ちなみに NestedText の仕様にもこうある

      Only ASCII spaces are allowed in the indentation. Specifically, tabs and the various Unicode spaces are not allowed.

  • 「YAML が悪いのではなく YAML 1.1 が悪かったのであって、しかも大半は 1.1 パーサだ」という理屈は、この記事が期待するほどには効かないと思う
    節度を持って使う YAML は好きだし、YAML 1.2 向けの高性能パーサが新しく出てくるのも歓迎だが、「悪い」バージョンが依然として多数派なら、私は別のものを使うと思う。どのパーサが使われるか制御できず、自分の YAML が正しく解釈されると信じられないなら、YAML 全体は依然として「悪い」状態だ
    みんなが 1.2 に移行すべきだが、それまではYAML を慎重に扱うことは妥当だと思う

  • 「YAML 対 TOML 論争は、たいていはもはや問題のある形では存在しない形式を攻撃する論争であり、不満は現実のものだが歴史的なものだ」という一文には、GitHub Actions と Kubernetesを前にして悲鳴を上げたくなる

  • 擁護の理屈は強い。それでも、ごく単純な文書ではTOML の方が読みやすく、人に YAML より TOML を使ってもらう方が簡単だ
    残念ながら、ツールに対する開発者の公的な認識を変えるには長い慣性がある。人は何かの話を読んで心を決め、その後は公の失敗をしていない別のツールへ移ってしまう

    • 配列も含めて構成が2段階を超えると、そうでもなくなる。その時点ではインデントが構造をずっと理解しやすくしてくれる
  • Ruby インタプリタに同梱されている YAML パーサがYAML 1.2.2をサポートしてくれたらと思う
    ただ、生態系を壊さずに新しいバージョンへデフォルトを切り替える方法はよく分からない

  • 設定ファイル形式が標準化されたスキーマを指定できるといい。そうすればエディタが任意の設定ファイルを見て、タイプミスしたキーや型の不一致を知らせられる
    キーが何のためのものかを「hover」ヒントで文書化し、有効なキーの自動補完も簡単に提供できるべきだ。無効な値を捕まえるための簡単なアサーションや契約までサポートできればなお良い。たとえば "color" キーは /#[a-fA-F0-9]{6}/ に一致しなければならない、といった具合だ
    理想を言えば、これで設定ファイルのデータ構造も自動生成できるべきだ

    • それは単にXMLをそのまま説明しているだけ
      XSDRelax NG のような各種の検証仕様形式があり、私は XML の DTD に最もなじみがあるので、他についてはあまり語れない
    • JSON ファイルでは最上位キーとして $schema 属性を置き、正しいスキーマを定義した JSON Schema ファイルを参照する方式がかなり一般的だ。本質的には JSON 向けの XSD である。良いエディタなら通常、これを基に自動補完や赤い波線を提供できる
      良い点は、TOML と YAML はだいたい文法だけが違う JSON なので、通常はJSON Schemaファイルも一緒に使えることだ