kubell Creator's Note

株式会社kubellのエンジニアのブログです。

ビジネスチャット「Chatwork」のエンジニアのブログです。

読者になる

Claude Code のためのコンテキスト設計で実現した Helmfile README の大量生成

はじめに

AIエージェントを使ってドキュメントを自動生成する際、単に「とりあえず書いて」とお願いするだけでは、十分な深さの内容を得るのが難しい、というのが私の実感です。

今回約60個のREADMEを整備した経験から、AIが実力を発揮できる土俵を整えることの重要性を痛感しました。必要な情報へのアクセス経路を用意し、何を書くか(What)をテンプレートで固定し、どう書くか(How)をスラッシュコマンドで定式化する——この3層構造を整えることで、READMEのような定式化可能なドキュメントを一貫性を保ちながら効率的に作成できました。

この記事では、Helmfileモノレポで約60個のREADMEを整備した事例を通じて、この取り組みのやり方を紹介します。

この記事は kubell Advent Calendar 2025 22日目の投稿です。SREグループの山下(@task2021)がお送りします。


背景: Helmfileモノレポのドキュメント課題

kubellでは、Helmfileを使って多数のコンポーネント(=Helmfileのrelease単位)を1つのリポジトリで管理しています。いわゆる「Helmfileモノレポ」構成です。

creators-note.chatwork.com

歴史が長くプロジェクトが成長してきたこともあり、このリポジトリにはかなりの数のコンポーネントが存在しています。一部省略してもざっと以下のような感じです。

$ pwd
/path/to/github.com/chatwork/helmfiles
$ tree -L 2
.
└── helmfiles
    ├── actions-runner-controller
    ├── argocd
    ├── argo-rollouts
    ...間にたくさんある
    ├── keda
    └── vault

一方で、時間の経過とともにドキュメントのメンテナンスが追いつかず、

  • READMEがまだ整備されていないコンポーネント
  • 導入当時の記録は残っているものの、その後の更新が反映されていないコンポーネント
  • 導入当時の背景を知るメンバーの入れ替わりがあったコンポーネント

など、ドキュメント面での課題が見られるようになっていました。

この結果、SREグループに新しく入ってきたメンバーから見ると、まずはその数に圧倒され、

  • 「このコンポーネントは何のために存在しているのか」
  • 「これはどういうコンポーネントなのか」
  • 「そもそも今も本番で使われているのか」

がREADMEからは分からず、結局実際の実装や過去のPR、ドキュメントを手で掘るしかない、という状況でした。 既存メンバー側も、「なぜこのコンポーネントが導入されたのか」という導入背景(Why)を完全に説明できないものもあり、運用上の不安要素になっていました。

そこで今回は、Helmfileのコンポーネントを対象に、Claude Codeを使ってREADMEを約60個まとめて整備する、という取り組みを実施しました。

この記事では、単に「Claude CodeにREADMEを書かせた」話ではなく、

  • AIエージェントが必要な情報にアクセスできるようにする「コンテキストの整え方」
  • READMEテンプレートで「何を書くか(What)」を固定する方法
  • スラッシュコマンド(エージェント)で「どう書くか(How)」を定式化する方法

を、Helmfileモノレポという具体例を起点にしつつ、Terraformやデータパイプラインなど他の構成リポジトリでも転用しやすい形で整理して紹介します。


この取り組みでやったこと(概要)

今回READMEを整備したのはHelmfileで管理された多数のコンポーネントですが、やったこと自体はTerraformやデータパイプラインなど他の構成リポジトリにもそのまま転用できると考えています。

大きくやったことは次の3つです。

  1. Claude Codeが「必要なときに必要なコンテキスト」にアクセスできる状態を整える
  2. READMEテンプレートを作り、章題ごとに「何を書くか(What)」を決めて固定する
  3. スラッシュコマンド(エージェント)で「どうやって書くか(How)」を明示する

この3つを組み合わせて、READMEを量産できる仕組みを作りました。以下では、それぞれを具体的に説明します。


ステップ1: Claude Codeが社内コンテキストにアクセスできる状態を整える

まず、Claude Codeに「書いてほしい内容」を渡す前に、そもそも必要な情報にアクセスできる状態を整える必要があります。

今回のREADME生成では、次のようなコンテキスト源を使いました。

SREグループでは、Atlassianが提供する生成AIであるAtlassian Rovoをふだんの業務でもかなり使っています。 RovoはAtlassianのGenAI製品で、JiraやConfluenceなどに散らばった組織内のナレッジを、Rovo検索/チャット/エージェントから横断的に扱えるようにするものです。

今回のREADME整備では、そのRovoの検索機能をAtlassian MCPのtools(Rovo search / fetch)経由でClaude Codeから叩いています。

これによって、「そもそもなぜこのコンポーネントが存在するのか?」を探る入口を、かなりの部分Rovo+Claude Codeに肩代わりしてもらえました。

kubellでは以前からAtlassian製品(Confluence / Jira)を利用しているため、社内には多くのデータが蓄積されています。 もちろん、使っているツールや管理方法は会社によってさまざまだと思います。ただ、どの環境であっても共通して言えるのは、エージェントが蓄積された社内データへアクセスできるように整えておくと、同じような取り組みを実現しやすくなるという点です。

今回の事例は、たまたまRovoやAtlassian MCPを活用した形ですが、ポイントは特定の製品に依存することではなく、自社のナレッジをエージェントが扱える状態にしておくことだと思います。

www.atlassian.com

Datadog MCPなどの監視系ツール

kubellでは監視にDatadogを使っているため、

  • どのモニターが関連しているか
  • どのダッシュボードを見ればいいか
  • 主要メトリクスやしきい値

といった情報を整理したい、と考えていました。

今回のREADME整備では、Datadogまわりの情報をDatadog MCP経由でClaude Codeから取得し、

  • 関連モニター
  • ダッシュボード
  • アラート設定

を検索してもらい、その結果をREADMEに反映するようにしています。

docs.datadoghq.com

Git / GitHubコマンド

モジュールの導入背景(Why)を把握するうえで、最初に追加されたPRや、大きな設計変更が入ったPRを追うことは非常に重要です。そこには「なぜそれを作ったのか」「どんな前提や制約があったのか」がかなりの確率で書かれています。 今回のREADME整備では、これらの情報収集をClaude Codeから直接git / ghコマンドを叩いてもらう形で行いました。

具体的には、モジュール名に関連しそうなPRを検索し、

  • 導入時のPR
  • 大きな変更を伴ったPR
  • PR内で参照されているConfluence / Jira / Issue

を順番にたどり、その内容を「導入背景」章に要約して反映しています。


ステップ2: READMEテンプレートを作成する – 章題ごとに「何を書くか」を明示する

次にやったのが、READMEのテンプレート作成です。 先に章題ごとに「何を書くか(What)」を決めて固定することで、Claude Codeが常に一貫性のある内容を出力できるようにしました。

固定した章立て

今回のテンプレートでは、ざっくり以下のような章立てにしました。

  • 概要(このモジュールは何をするか)
  • 導入背景(なぜ必要だったか)
  • 適用される環境/デプロイ先
  • 基本動作・構成
  • 監視・アラート
  • よくあるトラブル
  • 参考リンク

「What」だけをシンプルに書く

テンプレートでは、各章について「何を書くか(What)」だけを短く書いておきます。 一部抜粋すると、イメージは次のような形です。

# <component> モジュール README

# 概要
<何をするためのものか>(役割を一行で)

# 導入背景
<なぜこのモジュールが必要か。解決したい課題・影響範囲・他案との比較を簡潔に。可能なら対応チケットやPRで示された要求を一文で引用>

# デプロイ状況
確認日: <yyyy-MM-dd>

| 環境/クラスタ    | Namespace   | インストール状態 | 主な確認対象 / 備考 |
|------------------|------------|------------------|---------------------|
| prod             | <namespace> | installed=true/false | <備考>           |
| stg              | <namespace> | installed=true/false | <備考>           |
| test             | <namespace> | installed=true/false | <備考>           |

# 監視 / モニタリング
- モニタリングツール(例: Datadog)のモニターやダッシュボードがある場合はリンクと主要メトリクス名を記載
- しきい値・通知先が分かれば併記

...

ここでは具体的な調査方法や、どのツールから情報を取るか(How)は書きません。 テンプレートはあくまで「最終的にどういうREADMEを作りたいのか」という完成形の枠を決めるためのものだからです。

実際にどう調査し、どう埋めるか(How)は、後述するスラッシュコマンドの側にまとめて記述しました。


ステップ3: スラッシュコマンドで『どう書くか(How)』を定式化する

テンプレートで「What」を決めたら、次は「How」をスラッシュコマンドに落とし込むフェーズです。

Claude Codeのスラッシュコマンドとして、README作成の流れをそのまま手順化しました。

  1. ブランチ運用
  2. README作成(情報収集+テンプレ埋め)
  3. 確認・コミット・PR

ここから、「HelmfileのREADMEを量産したときに実際に使った定義」の一部を交えながら説明します。

3-1. ブランチ運用 – git worktreeを使って並列作業可能にする

READMEを60個近く追加・修正していくにあたり、

  • 1つのローカルクローンだけで作業すると、基本的に1つのモジュール分しか同時にいじれない
  • かといって、モジュールごとにリポジトリを複数cloneすると、容量的にも管理的にも効率が悪い

という事情がありました。

そこで、モジュールごとにgit worktreeを使う方針にしました。 スラッシュコマンド内では、次のようにその手順を明示しています。

## 1. ブランチ運用
- 作業前に現在のブランチを確認 (`git branch --show-current`)
- 新規作業はgit worktreeを使用して別のディレクトリで行う
    - `git worktree add ../helmfiles-{component} -b feature/add-readme-{component}`
- 作業ディレクトリを移動: `cd ../helmfiles-{component}`
- 作業完了後は元のディレクトリに戻り、worktreeを削除: `cd -``git worktree remove ../helmfiles-{component}`

これにより、

  • モジュールA・B・Cそれぞれ用の作業ディレクトリを用意しつつ
  • ベースのクローンは1つだけ

という状態を作れたので、レビュー単位でPRを分けながらも効率よく進めることができました。

3-2. README作成 – テンプレートの章ごとに「How」を書く

ここがこの記事の本題です。 READMEテンプレートが「What」だとすると、スラッシュコマンドは「テンプレートの各章ごとに、どうやって情報を集めるか(How)」を全部書いたドキュメントになります。

テンプレの各章ごとに、どのツールから何を取ってくるかを書く

たとえば、実際のスラッシュコマンドの一部はこんな形にしました(抜粋です)。 bashスクリプト部分は、手順イメージを伝えるための参考例として載せているだけなので、細かい内容は読み飛ばしても問題ありません。

## 2. README作成
以下の情報を集め、`helmfiles/README_TEMPLATE.md` をそのままの章立て・表形式で埋める(章順を変えない)。

- 概要 / 導入背景
    - `helmfiles/{component}/helmfile.yaml.gotmpl` でチャート名・リリース名・役割を一行で把握
    - MCPのRovo Searchを使い、コンポーネント名や別名でJira/Confluenceを横断検索(例: `component-name`, `component` AND `課題`, `component` AND `障害`)。対応チケットの「なぜ必要か」を優先的に抜き出す。
    - helmfile.yaml.gotmplを含むPRも参照し、PR文面内のリンク(Confluence/Jira/GitHub issues)を辿って要点を補完

- デプロイ状況(クラスタ・Namespace・installed判定・備考)
    1. 環境ディレクトリを列挙: `find environments -maxdepth 2 -type d -name "*-*"`
    2. helmfileのvalues配列をenvに置換し、yqでマージして `<componentKey>.installed` を確認
       ```bash
       component=<component-dir>
       componentKey=<componentKeyInValues>
       env=<environments>
       cluster=${env%%-*}
       values_files=(
         helmfiles/${component}/environments/values.yaml
         environments/${cluster}/settings.yaml
         environments/${cluster}/${env}/settings.yaml
       )
       yq ea '. as $item ireduce ({}; . * $item) | .["'"'"${componentKey}"'"'"] | .installed // "false"' "${values_files[@]}"
       ```
    - values配列がコンポーネントごとに異なる場合はhelmfile.yaml.gotmplに合わせて `values_files` を組み替える
    - クラスタ名のプレフィックス変更(例: eks133→eks134)があるため、列挙結果から最新プレフィックスを採用
    - サブキー(public/private/test等)がある場合は個別キーで評価し、備考に書く
    - READMEの表はテンプレートの `eksXXX-prod` / `eksXXX-stg` といった抽象表記を残し、具体数値(eks133等)に置き換えないよう注意する

- 監視 / モニタリング
    - ConfluenceやPR・テンプレートからDatadogモニター/ダッシュボードを探し、存在するものだけリンク付きで記載(主要メトリクス名・しきい値・通知先も分かれば追記。未設定・不明は書かない)

...

ここでやっているのは、

  • 「テンプレートの各章(概要/導入背景/デプロイ状況/監視…)ごとに」
  • 「どのツールから何をどう取ってくるか」を
  • スラッシュコマンドの中で明文化している

ということです。 上の記述はあくまで抜粋ですが、実際にはテンプレートの章題をすべてカバーするようにHowを書いています。

静的な情報は「読むのではなく動かして判断して」と指示する

構成管理リポジトリでは、設定ファイルが環境ごとにレイヤー構造になっていることがよくあります。

  • base設定
  • 環境ごとのオーバーレイ(prod / stg / testなど)
  • さらにクラスタ単位やサービス単位の上書き

この場合、

  • 「どの環境でこのモジュールが有効か」
  • 「どんな値が最終的に効いているか」

コードリーディングだけで追うのは辛いですし、 人間だけでなくClaude Codeに全部読ませて解釈させると間違う可能性も高いです。

そこでスラッシュコマンドの中では、静的な項目については次のように指示しました。

  • 設定ファイルを全部読んで推測するのではなく、

    1. 対象モジュールに関係する設定ファイルのパスを列挙する
    2. それらを指定した順にyqなどでマージする
    3. 最終的なフラグ値(enabled / installedなど)を読む
  • つまり、「読んで理解する」のではなく、「実際にマージしてから値を読む」シェルコマンドを生成・実行させる

こうしておくことで、

  • 多数のファイルをコンテキストに読み込む必要がなくなる(トークン節約)
  • AIの解釈ではなく「コマンドの結果」に依存するので、結果の再現性・信頼性が上がる
  • 人間が手動でやる場合も、同じコマンドを叩けば同じ結果が得られる

というメリットが得られました。

3-3. 確認・コミット・PR – 最後まで型にしておく

最後に、READMEを追加した後の「人間の作業」もスラッシュコマンドに含めました。

## 3. 確認・コミット・PR
- `git status` で変更を確認
- `git diff` でREADMEの内容をざっと確認し、明らかに事実と異なる記述がないかチェック
- `git add helmfiles/{component}/README.md`
- `git commit -m "docs: add {component} readme"`
- `git push origin feature/add-readme-{component}`
- PRを作成し、レビュー依頼先(例: SREグループ)を指定する

ここまで書いておくと、スラッシュコマンドを最後まで辿れば、とりあえずPRまではたどり着く状態になります。


効果と学び

今回の取り組みを通して感じたこと・得られたことをまとめておきます。

学び1: コンテキストエンジニアリングの重要性

私がこの取り組みで学んだのは、やはりAIエージェントが心置きなく働けるためのコンテキストエンジニアリングの重要性です。

  • Rovo / Datadog / gitなど、エージェントが叩けるコンテキストの入口をあらかじめ用意し、
  • READMEテンプレートで「最終的にどんなアウトプットがほしいか(What)」を固め、
  • スラッシュコマンドで「どのコンテキストをどう使うか(How)」を書き下す

という三層構造を整えたことで、AI側には「探索と要約」に集中してもらえるようになりました。

逆に言うと、

  • コンテキストにアクセスできない状態で「とりあえずREADME書いて」とお願いしても、どうしても浅い内容になりがちですし、
  • 手順やテンプレートが曖昧なままだと、「コンポーネントごとに書いてあることがバラバラ」という状態から抜け出せません。

その意味で、今回の取り組みは「AIをどう賢くするか」という話というより、AIが実力を出し切れるように人間側が土俵を整える作業=コンテキストエンジニアリングの重要性を再認識する機会になりました。

学び2: 「読ませる」より「動かして評価させる」

もう一つ、実務的な学びとしては、設定や構成情報をエージェントにひたすら「読ませて理解させる」のではなく、実際にコマンドやツールを動かして「最終的な状態」や「評価結果」を出させ、その結果だけを見てもらう方が安定する、ということです。

具体的には、

  • レイヤーが重なった設定ファイル群をそのまま読み解かせるのではなく、
  • 最終的な値や状態を算出するコマンド/スクリプトを実行してもらい、
  • その出力をもとにREADMEや説明を書いてもらう

というスタイルを今回は取りました。

この「動かして評価させる」パターンは、Helmfileに限らず、Terraformやkustomize、データパイプラインの設定など、環境ごとにレイヤーが重なって最終値が分かりにくい仕組みであれば広く応用できます。 エージェントに長大な設定を丸ごと読ませて推論させるのではなく、「最終状態を出すコマンド」や「評価用スクリプト」を標準化しておき、その結果をベースにエージェントと人間が一緒に考える、という設計にした方が、再現性と信頼性の両面で扱いやすいと感じました。