RubyにおけるYAMLコメント保持問題をどのように修正したか(そしてなぜ私たちがスポンサーになったか)

Jake Goldsborough
Feb 20, 2026 • 3 min read

5年間、私たちのインフラのランブックにはある問題がありました。YAMLファイルをプログラムで編集するたびに、すべてのコメントが消えてしまうのです。

問題

Discourseのインフラチームは、数百のYAML設定ファイルを管理しています。また、データベースのフェイルオーバー、クラスターの移行、クレデンシャルのローテーションといった操作のために、これらのファイルを変更するランブック(Rubyスクリプト)も持っています。

問題は、Rubyの標準YAMLライブラリ(Psych)がコメントを保持しないことです。

data = YAML.load_file('config.yml')
data['new_key'] = 'value'
File.write('config.yml', YAML.dump(data))
# Every comment in the file is now gone

これはバグではありません。YAMLの仕様では、コメントはパーサーが破棄してもよい「表示上の詳細」として扱われており、ほとんどのパーサーは実際にそうしています。

2021年までに、この問題は実際に痛みを引き起こすようになっていました。一部のランブックは「docker-hostingファイルをめちゃくちゃにしている」と表現されていました。コメントが消え、インデントが変わり、文字列が無作為にクォートされたりされなかったりしていました。

エンジニアは、ランブックを実行するたびに手動でコメントを追加し直すか、どうせ失われるとわかっているのでコメントを書くのをやめるかのどちらかを選んでいました。どちらの選択肢も良いものではありません。

以前の試み

同僚が2021年に、Psychの純粋なRuby実装であるpsych-pureを使ってこの問題を解決しようとしました。しかし、当時psych-pureは十分に安定していませんでした。本番ファイルのエッジケースでパースが失敗することがありました。プロジェクトは停滞しました。

私たちが検討した他のアプローチ:

  • 文字列操作: 正規表現による検索・置換。非自明なYAML構造では機能しなくなります。
  • Psych ASTの操作: PsychのASTを操作することで一部の情報を保持するよう誘導できますが、ドキュメントがなく脆弱です。
  • PythonからruamelをYAMLに移植する: 可能ではありますが、大規模な取り組みになります。

解決策: psych-pureへのスポンサー

2025年後半、Kevin Newtonはpsych-pureの積極的な改善を続けていました。私たちは、本番環境で使える状態にするための残りの作業にスポンサーとして参加することについて連絡を取りました。

psych-pureの重要な洞察は、YAMLの仕様でコメントが「表示上の詳細」とされていても、パーサーがコメントを保持することを妨げるものは何もないという点です。psych-pureは、コメントをAST内の隣接するノードに紐付けます。YAMLにシリアライズし直すとき、コメントも一緒に出力されます。

私たちはKevinにスポンサーし、ライブラリを安定させ、本番ファイルで失敗していたエッジケースを修正してもらいました。2つのリポジトリにまたがる1,736の本番YAMLファイルを対象に数回のテストを行った後、パース失敗はゼロになりました。

統合: YamlHelper

psych-pureが安定したことで、私はopsリポジトリにYamlHelperを構築しました。APIはシンプルです。

YamlHelper.edit_file('container.yml') do |data|
data['env']['NEW_KEY'] = 'value'
data['templates'] << 'new_template.yml'
end

これはファイルを読み込み、変更のために解析済みデータをyieldし、その後すべてのコメントを保持したままファイルに書き戻します。

以前は、多くのランブックがコメントを削除していました。今はもうそうではありません。

テスト

テストスイートには、ランブックが実行するすべての操作をカバーする11のテストがあります。クラスター管理、データベースのフェイルオーバー、S3キーのローテーション、コンテナ設定の編集などです。すべて合格しています。

一つの期待される動作として、削除されたキーに直接紐付けられたコメントも削除されます。database_urlを削除すると、database_urlが何をするかを説明するその上のコメントも消えます。これはpsych-pureの正しい動作です。
その他すべてのコメントは完全に保持されます。

なぜコメントが重要なのか

インフラ設定のコメントは、単なるドキュメントのためのドキュメントではありません。それは組織の知識です。

# DO NOT CHANGE - legacy app expects this exact port
port: 8080

# Increased from 512MB after OOM on 2024-03-15, see incident #423
memory_limit: 1024

# TODO: Remove after Rails 8 upgrade
legacy_cookie_format: true

これらを失うと、長年にわたって蓄積されたコンテキストが失われます。エンジニアはより悪い判断を下します。インシデントのデバッグに時間がかかります。属人的な知識は属人的なままになります。

コメントを破壊する自動化は、ドキュメントの作成を積極的に妨げます。次のスクリプト実行で削除されるとわかっているのに、なぜ回避策を説明するコメントを書くのでしょうか。

スポンサーシップモデル

Kevinにpsych-pureを完成させるためのスポンサーをすることで、Rubyエコシステム全体に恩恵をもたらす、完全にメンテナンスされたオープンソースライブラリが実現します。

これは理にかなったターゲットを絞ったスポンサーシップの一例です。具体的な問題、すでに解決策に取り組んでいる開発者、明確な成果物があり、Kevinとの協力は素晴らしいものでした。

psych-pureの使い方

gemはRubyGemsにあります。

gem install psych-pure

yaml-janitor

Rubyでインフラの自動化を構築していてYAMLファイルを扱っているなら、これは実際の問題を解決します。また、私はpsych-pureを利用したリンターであるyaml-janitorも構築しました。コメントを保持しながら書式の不整合を修正することができます。

リンク

原文はこちら:


Good Loopでは、Discourseのセルフホスティングを安価で提供しています。開発元であるCDCK社の協力のもと、公式ブログ記事の翻訳・公開など、日本での普及にも努めています。

詳しくはこちら: Discourseの導入・運用支援・コンサルティング – Good Loop