一言でいうと
YAMLは構造化データを書くための、人間に優しい方法です。親戚であるJSONなんかの波括弧やクォートの代わりに、きれいに整理された買い物リストみたいな、すっきりしたインデントを使います。
YAMLが解決する問題
はじめにカオスがあった。そして設定ファイルが生まれた。.iniのような初期のフォーマットはシンプルでしたが、複雑なネスト構造のデータは扱えませんでした。次にXMLが登場しました。パワフルで構造的でしたが、あまりに冗長でタグだらけ。まるで法律用語で書かれたIKEAの家具の説明書を読みながら組み立てるような感覚で、人間様は書くのが大嫌いでした。
その次にやってきたのがJSON (JavaScript Object Notation)で、これは大きな改善でした。軽量で、ほとんどのプログラミング言語のデータ構造に直接マッピングでき、XMLよりずっと目に優しかったのです。でも、人間が頻繁に書いたり編集したりする必要があるファイル——例えば、DevOpsのスクリプト、アプリケーションの設定、国際化テキストなど——にとっては、JSONの構文はまだ面倒な作業に感じられました。おびただしい数の波括弧、カンマ、クォーテーションマークは、見た目的にノイズが多くて、ミスもしやすかったのです。
そこでYAMLの登場です。この名前は「YAMLはマークアップ言語じゃない(YAML Ain't Markup Language)」の再帰的頭字語で、その精神を完璧に捉えています。画面を見つめる人間、ただその一点を最優先のターゲットとして、ゼロから設計されました。JSONと同じ基本データ構造(キーと値のペア、リスト、単純な値)を取り入れつつ、「これを表現するのに必要な最低限の構文って何だろう?」と問い直したのです。
その答えが、インデントでした。空白を使って構造を示すことで、YAMLはそれ自体がドキュメントになるほどクリーンなフォーマットを生み出しました。これは設定ファイルの世界のために作られたもの。そこでは、高スループットなAPIに求められるマシン最適化のニーズよりも、明瞭さや編集のしやすさが優先されるのです。
内部の仕組み
YAMLの「魔法」の正体は、インデントされたテキストを構造化データに変換するための、シンプルで一貫したルールの集まりに過ぎません。YAMLはJSONのスーパーセットなので、有効なJSONをYAMLファイルにコピペしても、たいていはそのまま動きます。でも、本当の強みは、そのネイティブでミニマリストな構文にあります。
基本要素:スカラー、シーケンス、マッピング
YAMLのすべてのデータは、突き詰めると次の3つの要素に行き着きます。
マッピング(辞書やオブジェクトとも呼ばれます): いわゆる
キー: 値のペアです。キーは文字列で、値には何でも入ります。別のマッピング、シーケンス、スカラーなどです。# シンプルなマッピング character: "Bilbo Baggins" race: "Hobbit" age: 111シーケンス(リストや配列とも呼ばれます): 順序付けられたアイテムのリストです。各アイテムはハイフンとスペース (
-)で示されます。# 文字列のシーケンス fellowship_members: - Frodo Baggins - Samwise Gamgee - Gandalf - Legolas - Gimliスカラー(単純な値とも呼ばれます): 文字列、数値、真偽値のような単一の値です。YAMLは賢いので、型をかなりうまく推測してくれます。
123は数値、trueは真偽値、Hello worldは文字列です。通常はクォートは不要ですが、文字列が誤って解釈される可能性がある場合(例:"true"、"1.23")は使うべきです。
秘伝のタレ:インデントと空白
これこそがYAMLで最も重要なコンセプトです。ネスト(入れ子)構造を示すための波括弧 {} や角括弧 [] はありません。代わりに、ただインデントするだけです。ルールは単純。ある行がその上の行よりも深くインデントされていれば、それは上の行の子要素になります。
基本要素を組み合わせてみましょう。これは所持品リストを含むキャラクターのプロフィールです。
# A nested structure
character:
name: "Gollum"
aliases:
- "Sméagol"
- "My Precious"
possessions:
- item: "The One Ring"
description: "A plain gold ring, surprisingly heavy."
- item: "A fish"
description: "Juicy and sweet!"
is_wretched: true
この構造を見てください。name、aliases、possessions、is_wretched はすべて character の下にインデントされているので、character のプロパティになります。aliases シーケンスは character マッピング内の値です。possessions シーケンスには2つのマッピングオブジェクトが含まれており、それぞれに item と description があります。
インデントの量は、同じブロック内で一貫している限り問題ありません。スペース2つがコミュニティの標準です。ただし、タブではなく必ずスペースを使ってください。 タブを使うのは、目に見えない苦痛の世界に足を踏み入れる一番の方法です。
高度なテクニック:アンカー、エイリアス、タグ
YAMLには、JSONにはないパワーユーザー向けの機能があります。ファイルをDRY(Don't Repeat Yourself - 繰り返しを避ける)に保つために設計されています。
アンカー (
&) とエイリアス (*):再利用したいデータのかたまりがある場合、アンカー (&anchor_name) で名前を付け、エイリアス (*anchor_name) で他の場所から参照できます。# デフォルトのユーザープロファイルをアンカーで定義 default_user: &default_user_profile theme: "dark" notifications: "enabled" permissions: "read-only" # デフォルトを継承する特定のユーザーを作成 users: - name: "Alice" # エイリアスでデフォルトプロファイルを読み込む <<: *default_user_profile # 特定のキーを上書き permissions: "admin" - name: "Bob" # Bobは標準プロファイルを使用 <<: *default_user_profileここで
<<は特殊なマージキーです。AliceとBobは両方ともデフォルトのプロファイルを取得しますが、Aliceのpermissionsキーは上書きされます。これは複雑な設定ファイルでは救世主のような機能です。タグ (
!!):YAMLは通常、型を推論しますが、タグを使えば明示的に指定できます。あいまいさを避けるのに役立ちます。例えば、数値の12.0ではなく、文字列の"12.0"が欲しい場合などです。version: !!str 12.0 # これを文字列として強制 not_a_boolean: !!str "no" # これを文字列として強制
現場あるある話
消えたパイプライン事件
ある若手DevOpsエンジニア、仮にクロエさんとしましょう。彼女は会社のCI/CDパイプライン(gitlab-ci.ymlファイルで定義)に新しいセキュリティスキャンを追加するタスクを任されました。彼女は新しいジョブを追加し、コードをプッシュしましたが…何も起こりません。パイプラインは実行されましたが、彼女の新しいスキャンジョブはどこにも見当たりません。失敗したわけではなく、ただ消えてしまったのです。2時間もの間、クロエさんはスクリプトの構文、ランナーの設定、フェーズの定義をチェックしました。ついに疲れ果て、彼女はシニアエンジニアに助けを求めました。シニアエンジニアはファイルを5秒ほど眺め、ある一行を指差しました。クロエさんは新しいジョブを、他のすべての場所で使われているスペース2つではなく、スペース3つでインデントしていたのです。YAMLパーサーはそれを、新しいトップレベルのジョブとしてではなく、前のジョブの不正な子要素とみなし、警告もなく無視していたのでした。
教訓: YAMLでは、空白が構文です。たった一つのスペースのズレが、ファイル全体の意味を変えてしまうことがあります。リンターや、データツリーを可視化してくれる構造化エディタを使って、こうしたエラーを即座に発見しましょう。
森のように増殖した設定ファイル
ある小さなスタートアップが、アプリケーションの環境(開発、ステージング、本番)を単一の config.yml で管理していました。最初はシンプルでした。しかし、prod-us、prod-eu、dev-feature-x といった環境が増えるにつれて、ファイルは爆発的に膨れ上がりました。データベースのURL、APIキー、フィーチャーフラグなどの巨大な設定ブロックが、わずかな変更だけで各環境にコピペされました。ファイルは500行のモンスターになり、タイムアウト設定のような共通の値を一つ変更するのに、5つの異なる場所で検索と置換が必要でした。新しく入社したエンジニアがこの状況を見て、YAMLのアンカーを導入しました。彼は共通設定をすべて含む &default_config ブロックを定義しました。そして、各環境の設定では、デフォルトをエイリアスで参照 (<<: *default_config) し、異なるいくつかの値だけを上書きするようにしたのです。500行あったファイルは100行未満に縮小されました。
教訓: 繰り返しは避けましょう。YAMLファイル内で大きなブロックをコピペしていることに気づいたら、アンカーとエイリアスを学んで使う時です。
ノルウェー問題
ある開発者が、ユーザーがドロップダウンから国を選択できる機能を構築していました。国コードのリストは、supported_countries: [ US, DE, UK, NO ] というシンプルなYAMLファイルに保存されていました。テスト中、ノルウェー (NO) のユーザーからサインアップできないと苦情が来ました。開発者は何時間もコードをデバッグし、変数を追跡しましたが、問題を発見できませんでした。フロントエンドから NO という値は正しく渡されています。ついに、YAMLファイルからロードされたデータを直接調べてみました。すると、彼のプログラム内の supported_countries 配列は ['US', 'DE', 'UK', false] となっていたのです。YAMLパーサーが(古いバージョンの仕様に従って)、クォートされていない NO を「偽 (false)」を表す真偽値として解釈してしまっていたのです。
教訓: 迷ったら文字列はクォートしましょう。数値 ("1.0")、真偽値 ("yes", "no", "on", "off")、あるいはその他の特別な値に見える可能性のあるスカラー値は、予期せぬパース結果を避けるために、明示的にクォートで囲むべきです。
よくある間違いと落とし穴
- スペースの代わりにタブを使う。 これはYAMLにおける最大の罪です。仕様ではタブは禁止されています。タブは目に見えないため、発見するのがめちゃくちゃ難しいパースエラーを引き起こす可能性があります。エディタでYAMLファイルに対してはスペースを使うように設定しましょう。
- インデントが不揃い。 あるリストアイテムがスペース2つで、次のアイテムがスペース4つでインデントされていると、ひどい目に遭います。構造が正しくパースされなくなります。インデントのレベルは一貫させましょう。
- あいまいな文字列をクォートし忘れる。 「ノルウェー問題」は典型例です。
Yes,No,true,false,On,Offのような文字列は真偽値としてパースされます。先頭にゼロが付く数字や特殊文字を含む数字も、誤って解釈される可能性があります。迷ったら、"クォート"で囲みましょう。 - 複数行文字列の混乱。
|(リテラルスタイル、改行を維持)と>(折りたたみスタイル、改行をスペースに変換)の違いを忘れること。これにより、苦心してフォーマットしたテキストブロックやシェルスクリプトが台無しにされることがあります。 - 予期せぬ
null値。 コロンの後に何もないキー (key:) はnull値になります。これは誤って削除してしまった場合によく起こり、コードがnullをチェックしていないと、警告なしのエラーを引き起こす可能性があります。
なぜ今、YAMLを知るべきなのか
2024年にコードを書くなら、YAMLからは逃れられません。YAMLは設定ファイル界の誰もが認める王様です。
- DevOpsとInfrastructure-as-Code: Kubernetes、Ansible、Docker Compose、GitHub Actions、AWS CloudFormationなど、数えきれないほどのツールが、主要な定義言語としてYAMLを使用しています。
- アプリケーション設定: 多くのフレームワーク(SymfonyやRuby on Railsなど)やアプリケーションが、設定ファイルにYAMLを採用しています。開発者が読んで修正するのが非常に簡単だからです。
- 静的サイトジェネレータ: JekyllやHugoのようなツールは、投稿やページのメタデータを定義するための「フロントマター」にYAMLを使用しています。
YAMLを知ることは、単に設定ファイルを書けるようになるということだけではありません。それは、あなたが扱うシステムの構造を理解することにつながります。微妙なインデントエラーを見つけたり、いつアンカーを使うべきかを知っていたりすることが、数分での修正と、デバッグで1日を失うことの分かれ目になるのです。
もっと詳しく
- YAML Spec 1.2.2: 公式の仕様書。内容は濃いですが、最終的なリファレンスです。
- Wikipedia: YAML: 言語の歴史、機能、バージョンについての優れた概要。
- Learn YAML in Y minutes: これさえあれば8割の場面で困らない、実践的なサンプル付きの素晴らしい1ページのチートシート。
- YAML Lint: やっかいな構文エラーを見つけ、パーサーがどのように「見ている」かを理解するのに非常に役立つオンラインバリデーター。
- GitHub Docs: Workflow syntax for GitHub Actions: 完全にYAMLで定義された複雑なシステムに関する、優れた実例。これを研究することで、多くの一般的なパターンを学ぶことができます。