木目に沿ってコードを書く:AIコーディングエージェントにリポジトリを肥大化させないスキル

Sep 22, 2026 によって シュテファン・モラヴィク に公開されました。
AI Agents Claude Code Developer Tools Code Quality
このページの内容 1 分で読める
  1. 1手短なまとめ
  2. 2これが向き合う失敗
  3. 3木目
  4. 4ポニーテール
  5. 5エージェントに実際にやらせる
  6. 6リポジトリで起きたこと、うまくいかなかった部分も含めて

手短なまとめ

  • AIコーディングエージェントはコードを追加するのは得意だが、そのコードがすでに存在することに気づくのは苦手だ。放っておくと、最初の1つごとに2つ目のヘルパーをリポジトリに与え、読者はどちらかを信頼するために両方を確認しなければならなくなる。
  • 解決策は、エージェントが従うように書き留めた2つの習慣だ。木目:同じ種類のものの最も近い既存例を見つけ、その形、場所、名前を踏襲する。ポニーテール:「これは存在する必要があるか」から「機能する最小限」までのはしごを登り、最初に成立する段で止まる。
  • どちらも今は公開スキルで、MITライセンスだ:GitHubのgrain-skill 。作成ルール、固定タグ付きのレビューパス、そしてエージェントに何を踏襲したかを言わせる締めの行の形が含まれている。
  • 8月以降の私たちのメインリポジトリでは、386件のissueとプルリクエストが、踏襲すべき模範例を名指しする計画コメントを持ち、約18,000ファイルのツリーに対して1,333件のコミットが積まれた。コードの35箇所には、意図的な簡略化とそのアップグレード経路を名指しする ponytail: コメントが付いている。
  • 居心地の悪い発見:エージェントはこのスキルをスキルとして呼び出した回数がゼロだった。ルールが機能したのは、私が同じ内容をプロジェクトのCLAUDE.md、ワーカーエージェントの定義、レビューチェックリストにも置いたからだ。ルールは、エージェントがすでに読む場所に置け。

以前にもこれについて一度書いたが、その結果は気に入らなかった。原則を述べていて、仕組みを述べていなかったからだ。これが仕組みを伴う版だ。

これが向き合う失敗

私たちのバックエンドからの3つの例。すべて実例で、すべてテストを通ったエージェント作成のプルリクエストから来ている。

ファクトリからアプリケーションサービスを直接取得する新しいRESTルート。リポジトリ内の兄弟ルートはすべて、ルート→コントローラ→サービス→リポジトリと進み、各ホップが注入される。これはチェーンをルート→サービスに畳んでいた。動いてはいた。しかし、その機能のリクエスト検証とレスポンス整形が生きる唯一の場所が存在しなくなり、次にその機能に触るエージェントは2つの形から選ぶことになった。

巨大なサービスが _payloads.py、_protocol.py、_tokens.py に分割され、それぞれが素の async def のモジュールで、サービスはインポートによって protocol.discover(...) を呼んでいた。ファイル長チェックはグリーンになった。しかし依存性注入が防ぐために存在するものが、そのまま戻ってきた。インポート時に選ばれる協力オブジェクト、モジュールにパッチを当てない限り差し替え不可能、サービスが何に依存するかを記すコンストラクタからは不在。

2つ目の正規化関数。すでに1つあった。エージェントはそれを見つけられず、ほぼ同じことをする8行を書き足し、今では異なるエッジケース挙動を持つ2つの関数が同じフィールドを正規化すると主張している。

これらはいずれも、テストが捕まえる意味でのバグではない。いずれも、エージェントが存在するコードよりも生成できるコードを選んだ結果だ。これを数百のプルリクエストにわたってやれば、リポジトリは形を失う。

FlowHuntロゴ

ビジネスを成長させる準備はできましたか?

今日から無料トライアルを開始し、数日で結果を確認しましょう。

木目

新しいコードは、周囲のコードと見分けがつかないものであるべきだ。このスキルはその一文を、エージェントが書く前に踏む5つのステップに変える。

まず模範例を見つける。エンドポイント、サービス、リポジトリ、クエリフック、ストアスライス、コンポーネントについて、最も近い既存のものを特定し、その形、ファイルの場所、命名をコピーする。パターンが存在するのに構造を発明しない。私たちのリポジトリでは branding スライスがバックエンドの指名された模範例だ。小さく、クリーンで、あらゆるレイヤーにまたがっているからだ。そしてスキルの表は、成果物の種類ごとに、どのファイルを開き何を踏襲すべきかを示している。

レイヤーを尊重する。プレゼンテーションはアプリケーションを呼び、アプリケーションはドメインを呼び、インフラストラクチャはドメインを実装する。それらの線を越える新しいインポートはアーキテクチャチェックに失敗する。レイヤーのドキュメントを読むこと。再導出しないこと。

局所の木目とリポジトリの木目。兄弟に合わせる。しかし兄弟自体が正典のチェーンから逸脱しているなら、そのドリフトをコピーするのではなく正典のチェーンに従い、逸脱を記録する。「このファイルの他の5つのルートと一致している」だけでは、6つすべてが正典スライスが使うレイヤーを飛ばしているなら不十分だ。比較が局所的すぎる。

正典のヘルパーを再利用する。ヘルパー、型、パターンがすでにリポジトリにあるなら、それを使う。2つの正規化関数は、すべての読者に両方を差分比較させる。

名前は意味を運ぶ。すでにコードにある語彙を再利用する。既存の概念に対する新しい名前は、読者を待つバグだ。

最後のテストは、私がレビュアーに与えるものだ。エージェントが書いた行を形だけで見分けられるなら、それは木目に逆らっている。

ポニーテール

怠惰とは効率的ということであり、不注意ではない。最良のコードは決して書かれなかったコードだ。まず問題を完全に理解する。変更が触れるすべてのファイル、本当のエンドツーエンドの流れ。そのうえでこのはしごを登り、最初に成立する段で止まる。

怠惰のはしご:存在する必要があるか、すでにコードベースにあるか、標準ライブラリか、ネイティブプラットフォームか、インストール済みの依存関係か、1行か、そしてようやく機能する最小限のコードか

このはしごには2つのルールが付いてくる。

バグ修正は症状ではなく根本原因だ。チケットは症状を名指しする。触ろうとしている関数のすべての呼び出し元をgrepする。共有関数内の1つのガードは、すべての呼び出し元のガードより小さな差分であり、チケットが名指しした経路だけを直せば兄弟の呼び出し元は壊れたまま残る。

差分を最小限に保つ。差分こそレビューが読むものだ。通りすがりのリファクタリング、リネーム、再フォーマット、「ついでに」の改善、タスクが要求していないファイルへの接触、いずれもなしだ。その一つひとつが本当の変更を埋めてしまう。差分が問題より大きいなら、削り戻す。気づいた改善はフォローアップのissueであり、このブランチへの余分なコミットではない。

そして限界について。「怠惰」は最も誤読されやすい言葉だからだ。信頼境界での入力検証、データ損失を防ぐエラー処理、セキュリティ、アクセシビリティの基本、ユーザーが求めたもの、これらを簡略化して消しては決してならない。理解に対して決して怠惰になってはならない。はしごは解決策を短くするのであって、読解を短くするのではない。誤った場所の小さな差分は2つ目のバグだ。自明でないロジックは、実行可能なチェックを1つ残す。ロジックが壊れたら失敗する最小のものだ。

エージェントが意図的にある段を選び、天井を知っているときは、それをコード内で言う:

# ponytail: global lock, per-account locks if throughput matters

今、私たちのリポジトリにはそういうものが35箇所ある。それぞれは、将来の読者が再構成しなければならなかったはずの決定であり、天井に達したときにどこを見るべきかの目印だ。

エージェントに実際にやらせる

文書の中の原則は行動ではない。この2つを行動に変えた3つの仕組みがあり、私はこの順に重要だと考える。

ブリーフが模範例を運ぶ。アーキテクトパターン では、メインモデルがワーカー用のブリーフを書き、そのブリーフのDesignフィールドが、踏襲すべき最も近い既存パターンと、最小の根本原因の変更を名指しする。ワーカーは木目を探す必要がない。手渡されるのだ。私たちのリポジトリ全体で、386件のissueとプルリクエストに対する491件の計画コメントとはこれのことだ。誰かが書く前に何を踏襲するかを名指しする計画ステップ。

エージェントは何を踏襲したかで締める。すべての実装者の報告は1行、mirrored: <path> で終わるか、新しいパターンを名指ししてなぜ兄弟が合わなかったかを述べる。これは安価で、私が加えた中で単独最良のレビュー支援だ。誤った種類のファイルを指す mirrored: 行は、開く前にその差分が誤っていると教えてくれる。欠けている行は、エージェントが見なかったと教えてくれる。

レビューは同じ基準を、固定された順序で強制する。私たちのレビュースキルには固定タグ付きの2つのパスがあり、そのタグこそが指摘をレビュアー間、エージェント間で比較可能にする。

2つのレビューパス:構造パスはdiverge、duplicate、layer、misplaced、reach-in、inconsistent、own、chainをタグ付けし、削減パスはdelete、stdlib、native、yagni、shrinkをタグ付けする。一貫性は削減に勝るため構造が先に走る

構造が先。これは最も近い模範例から逸脱しているか、既存のヘルパーを重複しているか、レイヤーを越えているか、誤った場所にあるか、別モジュールの内部に手を伸ばしているか、一貫しない名前を使っているか、注入すべきものの所有権を取っているか、正典の呼び出しチェーンを壊しているか?次に削減。何を削除でき、標準ライブラリで置き換えられ、ネイティブ機能で置き換えられ、投機的として捨てられ、縮小できるか?

順序は好みではなくルールだ。一貫性は削減に勝る。兄弟から逸脱したままにする削減は勝利ではない。読者は8行節約するために2つの形を学ばなければならなくなるからだ。構造を先に走らせれば、削減パスはすでに一致しているコードにしか触れない。

リポジトリで起きたこと、うまくいかなかった部分も含めて

8月1日以降、私たちのメインリポジトリは1,333件のコミットを取り込んだ。そのほとんどはレビュー下でエージェントが書いたものだ。その期間のissueとプルリクエストのうち386件が、踏襲すべき模範例を名指しする計画コメントを持ち、そうしたコメントは合計491件。正典のチェーンは新しいエンドポイントの既定の形であり、コントローラを飛ばす2つのレガシールートは、第2のスタイルではなく、廃止されつつある逸脱としてドキュメントに記載されている。

35件の ponytail: コメントは私の予想より少ない数だが、それは正しいと思う。ほとんどの簡略化はコメントに値しない。値するものは、天井のあるものだ。

さて、私がむしろ報告したくない部分だ。私はgrainとponytailをClaude Codeスキルとして出荷した。スキルインデックスの説明、オンデマンドで読み込まれる本体、そしてコードを書く前にこれを呼び出せと書いたCLAUDE.mdの一行。5週間のトランスクリプトを通じて、エージェントがそれを名前で呼び出した回数はゼロだった。mirrored: の締めの行は7つの応答に現れた。

それでもルールは定着した。定着したのは、同じテキストがエージェントが選ばずに読む3つの場所にもあったからだ。最初のターンからコンテキストにあるプロジェクトのCLAUDE.md、検討すべきスキルとしてではなくルールとして「最も近い既存例の形、場所、命名を踏襲せよ」「実際に機能する最も怠けた解決策に手を伸ばせ」と書いてあるワーカーエージェントの定義、そしてタグを無視するプルリクエストを落とすレビューチェックリスト。スキル本体は、この3つが指す正典の参照となった。トリガーとしては、何もしなかった。

だから、エージェント向けに規約をパッケージングする人に伝えたい教訓はこれだ。スキルはルールの長い版を置く場所としては良い。ルールそのものを置く場所としては悪い。エージェントはそれを読み込むことを決めなければならず、たいていは決めないからだ。1段落の版は常にコンテキストにあるファイルに置き、命令形の版はコードを書くエージェントの定義に置き、チェックはマージをゲートするレビューに置く。そして詳細のためにその3つすべてをスキルに向ける。

github.com/LivinTribunal/grain-skill の公開版はそのように構成されている。エージェントが頭に保持できるルールを書いた短いSKILL.md、はしごとアンチパターンを書いた完全なドキュメント、レビュータグ、そしてどの段落があなたのCLAUDE.mdに入り、どれが実装者の定義に入るかを述べたREADME。実例を作業済みの例からあなた自身の正典の模範例に置き換えれば、それで完了だ。

よくある質問

シュテファンはFlowHuntを開発するAIおよびソフトウェアエンジニアです。製品そのものを超えて、彼は開発コストを削減しながらコード品質を向上させる、開発者向けのエージェント的ソフトウェアエンジニアリングワークフローを設計しています。

シュテファン・モラヴィク
シュテファン・モラヴィク
AIおよびソフトウェアエンジニア

チームがすでに守っている規約をエージェントに与えよう

FlowHuntなら、エージェントが従うべきルール、例、レビューの手順をあらゆるワークフローに紐づけられるので、その出力はコードを置く先のコードに自然に合います。