0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Code の常時ロードが 3,366 行に膨れていた話 ─ CLAUDE.md は守っていたのに気づかず肥大化していた

0
Posted at

cover.png

CLAUDE.md 本体は公式推奨の 200 行以内に収めていた。それなのに、.claude/rules/ 配下にルールファイルを 1 本ずつ足していった結果、毎セッション自動で注入される常時ロード層が 17 ファイル・3,366 行・約 207KB まで膨れていた。この記事は、その肥大化にどう気づき、常時ロードを 1 ファイル 73 行の「ルーター表」に作り替え、詳細ルールを「必要なときだけ読む」構造へ移すまでの実録である。

先に断っておくと、これはテクニック集ではない。私自身が Claude Code で仮想組織を運営していて、その運用ルールを積み重ねた結果じわじわ膨れていた常時ロードを、実測 → 気づき → 設計 → 検証まで一本の時系列で書く。検証の最後に残った「これで本当に効いたのか、次のセッションでしか確認できない」という不確実性も隠さず書いた。

きっかけは、X で見かけた投稿だった。利用期限のある上位モデルを使えるうちに、一時しのぎではなく「永久に効果が残る」運用制度そのものを整備しておくといい、という趣旨の投稿だ。期限が切れる前に一度、腰を据えて自分の仮想組織のルール構造を洗い直そうと思った。そうして棚卸しを始めた途中で、この記事に書く肥大化箇所にぶつかった。制度を整えるつもりで開けたら、整える以前に「毎セッション、これを全部読ませていたのか」という事実のほうが先に目に飛び込んできた、という順序だった。

なお、直近で工数見積もりの分布分析についての運用制度記事を書いたが、あれは「1 チケット単位の計測データ」の話だった。今回は「毎セッション自動で課金される常時ロード層そのものの構造」の話で、扱っているレイヤーが違う。同じ「仮想組織の運用」でも、片方はログの分析、こちらは注入されるコンテキストの土台そのものを組み替えた話だと思ってもらえればいい。

「守っていたはず」の CLAUDE.md が、実は 3,366 行を毎回読ませていた

結論から書くと、私の環境では CLAUDE.md 本体は 200 行以内に収まっていたのに、.claude/rules/ 直下に置いた 17 ファイル・合計 3,366 行・約 207KB が、毎セッション自動で注入されていた。CLAUDE.md の行数だけを気にしていて、その隣のディレクトリが同じ「常時ロード」の対象だと意識していなかった、というのが肥大化の正体だった。

Anthropic の公式ドキュメントは、CLAUDE.md について「1 ファイルあたり 200 行以内を目安に。長いファイルはコンテキストを消費し、遵守率を下げる」("target under 200 lines per CLAUDE.md file. Longer files consume more context and reduce adherence.")と明記している。私はこの 200 行を守っていた。だから「コンテキストは節約できている」と思い込んでいた。

抜け穴だったのは .claude/rules/ の使い方だ。運用ルールが増えるたびに、種別ごとに .md を切り分けて .claude/rules/ 直下に置いていった。1 ファイルずつは短い。だが公式ドキュメントによれば、この直下に置いたルールも CLAUDE.md と同様に毎セッション読み込まれる。「1 本ずつ短く分けている」という感覚と、「合計が毎回注入される」という事実の間に、私はズレを抱えたまま運用を続けていた。

実際に旧構成をバックアップから数え直すと、以下の通りだった(wc -l / wc -c で実測、2026 年 7 月上旬時点)。

対象 実測値
.claude/rules/ 直下のファイル数 17 ファイル
合計行数 3,366 行
合計バイト数 207,724 bytes(約 207KB)

1 ファイル 200 行以内という公式の目安は、あくまで「1 ファイルあたり」の話だ。私はそれを「自分の常時ロードは大丈夫」と読み替えてしまっていた。合計で 3,366 行、これが毎回、何をするセッションでも最初に読み込まれていた。

なぜ気づけなかったのか ─ 「毎回読む」と「必要なときだけ読む」の違い

気づけなかった理由は単純で、常時ロードされるものと、必要なときだけ読まれるものの区別を、自分の頭の中で持っていなかったからだ。公式ドキュメントを読み直すと、Claude Code のメモリ系ファイルには明確に挙動の違いがある。

ファイル / 仕組み 読み込みタイミング(公式ドキュメントより)
CLAUDE.md システムプロンプトの一部ではなく、システムプロンプト後のユーザーメッセージとして配信される("delivered as a user message after the system prompt")
.claude/rules/ 直下 毎セッション読み込まれる(だから短く保つ必要がある)
.claude/rules/paths: 指定 該当ファイルを触るときだけコンテキストに読み込まれる("only load into context when Claude works with matching files")
auto memory(MEMORY.md) 最初の 200 行、または 25KB のいずれか早い方のみ毎セッション読み込まれる("The first 200 lines of MEMORY.md, or the first 25KB, whichever comes first")

出典はいずれも Anthropic の公式メモリドキュメントで、原文を確認した(paths: によるスコープ限定などはドキュメント上 Claude Code の比較的新しいバージョンの機能として言及されている。環境によっては使えない可能性がある点は注意してほしい)。

ここで大事なのは 3 番目の行だ。.claude/rules/ は、直下にベタ置きすれば毎回読まれるが、paths: frontmatter でファイル種別にスコープを切れば「そのファイルを触るときだけ読む」に変わる。つまり肥大化を招く仕組みではなく、遅延ロードのために最初から用意されていた仕組みだった。私はその半分(ベタ置き)だけを使って、もう半分(スコープ限定)を使っていなかった。

MEMORY.md が「200 行 or 25KB のいずれか早い方まで」という上限を持っているのも、同じ思想だろう。全部を毎回読むのではなく、頭から一定量だけ読む。この「毎回読む量には上限がある」という前提を、私は .claude/rules/ に対してだけ適用し忘れていた。

やったこと ─ 常時ロードを 1 ファイルのルーター表に畳む

やったことは 1 行で言える。常時ロードされる .claude/rules/ 直下を、73 行の「ルーター表」1 ファイルだけに絞った。詳細ルールは同じディレクトリの外へ退避し、「この作業をするときはこのファイルを読め」という対応表だけを常時ロードに残した。

before / after はこうなった。

before after
.claude/rules/ 直下 17 ファイル / 3,366 行 / 約 207KB 1 ファイル(ルーター表)/ 73 行
CLAUDE.md 本体 200 行以内(元から遵守) 77 行のインデックスに再設計 / 上限 150 行
詳細ルール(17 本) 毎セッション全部注入 別ディレクトリに退避し、該当作業のときだけ Read

ルーター表の中身は「作業のトリガー → 読むべきファイル」の対応表だ。たとえば「記事を書く」なら執筆系のスロップ検出ルールを読む、「Obsidian に書き込む」ならフロントマターのルールを読む、という形で 1 行ずつ並べてある。作業に着手する前にその 1 行を見て、必要な詳細ルールだけを Read する。読んだら「準拠ルール: ◯◯」と 1 行残す運用にした。

実際のルーター表は、こういう見た目のテーブルを常時ロードのファイル(00-core.md)に置いているだけだ。作業内容が左に該当したら、右のファイルを読んでから着工する、という対応表になっている(プロダクト固有の行は伏せて、汎用的な行だけ抜粋する)。

作業トリガー 必読ファイル
記事執筆・SNS 文面の作成 marketing-content-slop.md と marketing-anti-slop-detectors.md
SEO / 技術記事(Qiita / note) 上記に加えて marketing-aeo.md
Obsidian への書き込み obsidian-vault-frontmatter.md
スパイク / PoC engineering-spike-mode.md

そして表の末尾にはこう書いてある。「該当なし(TODO・メモ・雑談)→ ルールファイルは何も読まなくてよい」。この 1 行が実は肝で、以前は雑談レベルの作業でも 3,366 行が全部注入されていた。ルーター表に畳んでからは、何もしないという選択肢が常時ロードに明記されるようになった。この表を初めて 1 枚に並べたとき、「今まで毎回、この 4 行で足りる作業にも全部読ませていたのか」と、正直かなり無駄をしていた実感があった。

CLAUDE.md 本体も、ルールをベタ書きする場所ではなくインデックスに作り替えた。「詳細はどのファイルにあるか」の地図だけを 77 行で持ち、本体の上限を 150 行に決めた。地図と本文を分離したイメージだ。地図(常時ロード)は薄く保ち、本文(詳細ルール)は必要なときに開く。

賢いモデルが作った制度を、安いモデルでも回せる形にする

この作り替えには、行数を減らす以外にもう 1 つ狙いがあった。上位モデルが設計した運用ルールを、安いモデルでも同じ判断で回せる形に外化することだ。私の仮想組織は 1 つのモデルで動いているわけではなく、探索やファイル特定のような軽い作業は安いモデルに任せ、設計判断やレビューの裁定は上位モデルが持つ、という役割分担で動かしている。

役割分担をすると、当然ながら「安いモデルはどこまで自分で判断していいのか」という問題が出てくる。ここで効いたのが、判断基準を文章ではなくチェックリストとして外に書き出しておくことだった。「この条件に当てはまったら自分でやらず委任する」「この作業のときはこのルールを読む」といった判断を、暗黙知ではなく明示的な対応表にしておく。そうすれば、どのモデルが引いても同じ分岐になる。

念のため補足すると、これは「AI に丸投げして自分は考えない」という話ではない。むしろ逆で、判断の質をモデルの賢さに依存させないために、判断の中身をこちらで構造化しておくという話だ。賢いモデルが暗黙的にやっていた判断を、安いモデルでも辿れる明示的な手順に落とす。思考を放棄しているのではなく、思考の結果を再利用可能な形に固めている。常時ロードをルーター表に畳んだのも、この「判断を外化する」方針と地続きだった。

移しただけでは効いたか分からない ─ 残った不確実性

ここが、この記事でいちばん正直に書きたかった部分だ。ファイルを移して行数を減らした直後は、それが本当に効いているのか確認できなかった

理由はコンテキストの読み込みタイミングにある。作り替えを行ったセッションの中で動いていたサブエージェントを見ると、旧構成の 17 ファイルがまだ注入されたままだった。おそらくセッション開始時点のスナップショットを持っていて、途中でファイルを差し替えても、そのセッション内の子プロセスには反映されない。つまり「移設が効いたかどうかは、次に立ち上げる新しいセッションでしか確認できない」という状態で作業が終わった。

正直、この時点では「畳んだつもりだけど、次に開いたら何も変わっていないかもしれない」という気持ちがあった。行数を減らすこと自体は wc -l で確認できる。だが「毎セッションの注入量が実際に減ったか」は、別のセッションを新しく立ち上げて中身を確認するまで検証できない。数字の上では改善しているのに、効果は未来のセッションに預けるしかない、という宙ぶらりんだった。

後日、新しいセッションを立ち上げて 2 つの経路で確認した。1 つは新規セッションで常時ロードの中身が実際にルーター表 1 ファイルに絞られているか。もう 1 つは、そこから起動したサブエージェントに旧ファイルが混入していないか。

確認してみると、削減自体は効いていた。ただ正直に言うと、想定通りに動いていない部分も見つかった。「畳んだのだから当然すべて綺麗に絞られているはず」と思っていた箇所が、実際には意外と不完全で、こちらの想定と食い違っている部分が残っていた。数字の上では減っているのに、細部まで意図通りとは限らない ── 完了と判定する前に新セッションで自分の目で見て回って、ようやくこの食い違いに気づけた、というのが実際のところだ。

この「移設は次のセッションでしか検証できない」という性質は、Claude Code のコンテキスト管理を触る人なら誰でもぶつかると思う。常時ロードの変更は、変更したセッションではなく次のセッションに効く。だから作り替えの完了報告は「行数を減らした」ではなく「次のセッションで注入量が減っていることを確認した」まで含めて初めて完了、という運用に変えた。

サブエージェントの「完了しました」は主張であって事実ではない

もう 1 つ、今回の作り替えを通じて改めて硬いルールにしたことがある。サブエージェントの「完了しました」は主張であって、事実ではない。だから採用する前に、grep なり read-back なり実行なりで実体を確認する。

これは今回の移設に限った話ではなく、過去に何度か「完了報告を鵜呑みにして痛い目を見た」経験から一般化した規律だ。委任した子プロセスが「やりました」と返してきても、それは実際に成果物ができていることを保証しない。ファイルが本当に書き換わっているか、指定した値が本当に入っているかは、こちら側で読み返すか実行して確かめないと分からない。

常時ロードの作り替えとこれが繋がるのは、まさに前節の「次のセッションでしか確認できない」問題があるからだ。「移設しました」という報告を信じるのではなく、新しいセッションを開いて注入量を目で見る。完了の定義を「作業した」から「結果を証跡で確認した」にずらす。行数を畳む作業を通じて、この検証の癖が自分の中で一段固くなった。

まとめ ─ 削ったのは行数ではなく「毎回読ませる判断」

冒頭の 3,366 行に戻る。あの数字は、私が「CLAUDE.md は 200 行以内」という 1 ファイルの目安を、自分の常時ロード全体の話だと勘違いしていたことの記録だった。実際に削ったのは行数そのものというより、「どの作業でも最初に全部読ませる」という判断そのものだ。常時ロードにはルーター表だけを残し、詳細は必要なときに開く。それだけで、毎セッションの土台がずっと軽くなった。

正直に限界も書いておく。トークン消費が実際に何割減ったかは、私は実測できていない。行数とバイト数は数えたが、それがそのままセッションあたりのトークン削減量になるわけではないし、推定値を出して「◯割削減」と断言するのは避けたい。ここは「常時ロードの中身が減ったことは確認できたが、金額換算の効果は未測定」という状態のまま置いておく。

一方で、転用できる構造ははっきりしている。常時ロード層は薄いルーター表に保ち、詳細ルールは作業種別ごとに条件付きで読む。そして判断基準は、モデルの賢さに依存させず、どのモデルが引いても同じ分岐になるよう外化しておく。この 2 つは、私の仮想組織に限らず、CLAUDE.md と .claude/rules/ を育てている人なら同じ形で持ち込めるはずだ。

ルールを 1 本ずつ足すのは気持ちがいい。だが足したルールは、毎セッション読まれているかもしれない。次に .claude/rules/ にファイルを足すときは、ls | wc -lwc -l *.md を一度打ってみてほしい。合計が自分の想像より大きかったとしたら、それは削るタイミングだ。

よくある質問

Q: CLAUDE.md は何行以内に収めるべきですか?
A: Anthropic の公式ドキュメントは「1 ファイルあたり 200 行以内」を目安としている。私は本体を 77 行のインデックスに作り替え、150 行を上限に設定した。行数を絞るだけでなく、本体には詳細ルールへの「地図」だけを残すと管理しやすい。

Q: CLAUDE.md を短くしたのに、コンテキストが肥大化するのはなぜですか?
A: .claude/rules/ 直下に置いたファイルも、CLAUDE.md と同様に毎セッション常時ロードされるため。私の環境では 17 ファイル・3,366 行・約 207KB が、CLAUDE.md とは別に気づかず注入されていた。CLAUDE.md の行数だけを見ていると見落とす。

Q: 常時ロードが肥大化したら、どう直せばいいですか?
A: 常時ロードを 1 ファイルの「ルーター表」(作業トリガー → 読むべきファイルの対応表)に絞り、詳細ルールは作業種別ごとに条件付きで Read する構造に移す。.claude/rules/paths: frontmatter を使えば、該当ファイルを触るときだけルールを読み込めるので、遅延ロードとして活用できる。

Q: サブエージェントへの委任で気をつけることは何ですか?
A: 完了報告は主張であって事実ではない、と割り切ること。子プロセスが「完了しました」と返しても、grep / read-back / 実行で成果物の実体を確認してから採用する。特に常時ロードの変更は、変更したセッションではなく次の新しいセッションでしか効果を確認できない点に注意する。

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?