← 기사 목록
日本語https://zenn.dev/topics/llm/feed

推測→変更→また壊れる:コーディングエージェントの悪循環に /tdd と /diagnose を差し込む

추출된 키워드

30
/tdd·5/diagnose·5AIコーディングエージェント·5コーディングエージェント·5Claude Code·4mattpocock/skills·4TDD·4Test-Driven Development·4テスト駆動開発·4Signal·4Diagnose スキル·4Instrument·3LLMエージェント·3Postmortem·3回帰テスト·3テストダブル·3private method·3public interface·3GREEN·3RED·3RED/GREEN/REFACTOR·3mock·2スタブ·2フェイク·2npx skills@latest·2/setup-matt-pocock-skills·2issue tracker·2triage labels·2CONTEXT.md·2ADR·2

원문

8,519
推測→変更→また壊れる:コーディングエージェントの悪循環に /tdd と /diagnose を差し込む

推測→変更→また壊れる:コーディングエージェントの悪循環に /tdd と /diagnose を差し込む

直したはずなのに、また壊れる

AIコーディングエージェントに「このバグを直して」と頼む。

数分後、エージェントは「修正しました」と返してくる。
動かしてみると、直っていない。別の場所が壊れていることもある。

もう一度渡す。
エージェントはコードを読み、原因を推測し、変更を加える。
そして、また壊れる。

この流れでは、変更が正しいかどうかを判定する材料が足りません。

人間のエンジニアは、変更のたびに実行して確かめます。
テストを走らせる。ログを見る。CLIを叩く。画面で確認する。

エージェントにも同じ確認をさせる必要があります。

その確認の手順を、毎回プロンプトで細かく書くのは面倒です。
そこで使えるのが、エージェントに作業手順を渡す スキル です。

この記事では、実装中に使う

/tdd
と、壊れた挙動を追うときに使う
/diagnose
を扱います。
どちらも、エージェントが推測で進みやすい場所に pass/fail の判定基準を差し込むための手順です。

エージェントスキルとは

Claude Code などの AIコーディングエージェント は、指示に従ってコードを書いたり、デバッグしたりできます。
何も設定しなければ、作業手順はエージェントに任されます。

mattpocock/skills は、その手順を渡す小さなファイルの集まりです。

https://github.com/mattpocock/skills

/tdd
/diagnose
といったコマンドを打つと、エージェントが対応するスキルファイルを読み込み、決まった手順で作業します。

/tdd
は、これから作る機能に判定基準を置きます。
/diagnose
は、すでに壊れている挙動に確認手段を置きます。

エージェントの作業をすべて縛るのではなく、失敗しやすい場面に確認ループを足す設計です。

問題の構造:エージェントに pass/fail の信号がない

LLMエージェントにデバッグを任せると、よくこうなります。

  • 「このバグを直して」と渡す
  • エージェントがコードを読んで推測する
  • 変更を加えて「直しました」と言う
  • 動かない。また渡す
  • 2〜4 を繰り返す

「直して」とだけ渡すと、エージェントは修正案を出す方向に進みます。
その修正が正しいかどうかを確認する工程は、指示しないと抜けることがあります。

「この判定基準を満たすように直して」と渡すと、エージェントは確認しながら進めます。

/tdd
/diagnose
が差し込むのは、この確認ステップです。

/tdd
が差し込むもの

TDD(Test-Driven Development、テスト駆動開発)では、RED/GREEN/REFACTOR という流れで小さく進めます。
失敗するテストを書き、そのテストを通し、必要なら実装を整える、という順番です。

この記事で扱う

/tdd
の使い方も、この考え方をエージェント向けに使います。
大きな仕様をまとめて渡すのではなく、1つの振る舞いをテストで表し、1つ実装する小さなループに分けます。

エージェントに効くのは、テストを書くことだけではありません。
順番も効きます。

先に失敗するテストを書く。
そのテストが失敗することを確認する。
そこから実装する。
最後にテストが通ることを確認し、必要ならリファクタリングする。

この順番を守ると、エージェントは「何を満たせばよいか」を見失いにくくなります。

RED/GREEN とは

TDD では、テストの状態を2色で表します。

  • RED:テストが失敗している。実装がまだ存在しないか、テストが表す振る舞いを満たしていない。
  • GREEN:テストが通っている。少なくとも、そのテストが表す振る舞いは満たしている。

/tdd
はまず RED から始めます。

「今の実装でこのテストが失敗する」ことを確かめてから、実装に入ります。
エージェントは、現在のコードの状態を基準にして作業できます。

最初から実装に飛ばず、まず失敗する判定基準を作ります。

全テストを先に書かせると何が起きるか

「全テストを先に書いて実装して」と指示すると、存在しない関数の仕様を想像してテストを書くことがあります。

そのあと、自分で書いたテストに合う実装を作ります。
テストは通ります。

ただし、それが本当に必要な振る舞いかどうかは別問題です。

1つずつ進めると、各テストが動作確認を経た仕様として積み上がります。

  • まず1つ失敗させる
  • その1つを通す
  • 必要なら実装を整える
  • 次の1つに進む

この小さいループが、エージェントの推測を抑えます。

外から見える振る舞いをテストする

コードには、利用者や他のモジュールから見える入口と、内部でしか使わない処理があります。

外から呼び出せる契約や入口を public interface と呼びます。
内部の補助関数や private method は、実装の詳細です。

private method のテストを書くと、内部実装を変えるたびにテストが壊れます。
リファクタリングのたびに、テストも書き直すことになります。

public interface に対するテストは、「何を受け取って何を返すか」という契約を検証します。
内部の実装を変えても、入出力が同じならテストは壊れません。

エージェントも、実装の形ではなく、外から見える振る舞いに合わせてコードを直せます。

なるべく本物のコードパスを通す

テストダブルとは、DB、外部 API、内部の協力オブジェクトなどの代わりに置く偽物の総称です。
mock、スタブ、フェイクなどが含まれます。

テストダブルを使うと、テストを速く、独立して動かせます。
一方で、依存先の実装を迂回するため、コンポーネント間の結合部で起きるバグを見逃すことがあります。

/tdd
は、なるべく本物のコードパスを通した検証を優先します。

エージェントが書いたコードは、単体では正しく見えても、実際の呼び出し経路で壊れることがあります。
本物のコードパスを通すと、そのズレを見つけやすくなります。

/diagnose
が差し込むもの

/diagnose
は、エージェントにすぐ修正させないための手順です。

壊れているコードを見ると、エージェントは原因を推測して変更しがちです。
再現条件も判定基準もないまま変更すると、「何を直したのか」も「直ったのか」も曖昧になります。

Diagnose スキル の流れは、おおよそ次の形です。

Feedback loop → Reproduce → Minimise → Hypothesize → Instrument → Fix + regression test → Cleanup + Postmortem

ここでは、最初の feedback loop を Signal と呼びます。
Signal は、成否を見分けるための判定基準です。
先に判定基準を決め、そのあとで再現し、再現を小さくしてから、仮説を立て、計測して、修正します。
適切な継ぎ目がある場合は、修正前に回帰テストを作って失敗を確認します。なければ、その理由を記録します。

Signal を最初に作る

まず、「この入力に対してこの出力が出れば正常、出なければ異常」という基準を決めます。

テスト、CLI の出力、ログの特定行など、エージェントが成否を機械的に判定できる形なら使えます。

確認手段なしで調査を始めると、コードを読んで推測します。
そのまま変更し、「直しました」と言います。

Signal を先に作ると、エージェントは変更後に同じ判定基準を確認できます。

再現してから触る

次に、バグを再現します。

再現できないバグを直そうとすると、エージェントはコードの見た目だけで原因を探します。
その結果、関係のない場所を変えることがあります。

再現できたら、できるだけ小さい形に絞ります。
入力、手順、関係するコードを減らしてから修正に入ると、変更前と変更後を比べやすくなります。

  • 変更前は失敗する
  • 変更後は通る

この差分が、修正の根拠になります。

仮説を 3〜5 個立てる

/diagnose
は、1つの推測にすぐ飛びつきません。
複数の候補を並べてから調べます。

たとえば、こうです。

  • この変数が
    null
    かもしれない
  • この関数が呼ばれていないかもしれない
  • この型変換が失敗しているかもしれない
  • この分岐に入っていないかもしれない

仮説を並べたあと、どれが正しいかをログや assert で確認します。

これにより、エージェントが最初の推測だけで大きな変更を加えるリスクを下げます。

Instrument で確かめる

Instrument は、確認のためにログや assert を入れて、実際の状態を観測する工程です。

エージェントに「原因を考えて」と渡すと、コードを読んで推測します。
「この仮説を確かめて」と渡すと、観測する方向に進みます。

デバッグでは、正しそうな説明より、実際に起きている事実のほうが役に立ちます。
Instrument は、その事実を取りに行く工程です。

回帰テストと片付けまで進める

小さくした再現を回帰テストにできる継ぎ目があるなら、修正前にテストとして残します。
まずそのテストが失敗することを確認し、それから修正し、最後に通ることを確認します。

適切な継ぎ目がない場合は、無理にテスト化せず、その理由を記録します。
一時的に入れたログ、assert、デバッグ用の出力は、役目が終わったら片付けます。

修正だけで止めると、次に同じ壊れ方をしたときにまた人間が気づく必要があります。
回帰テストがあれば、エージェントも人間も同じ確認手段を使えます。

再発を防ぐ設計を考える

Postmortem では、「どう直したか」だけでなく、「どんな設計なら防げたか」を考えます。

たとえば、次のような観点です。

  • 型で防げなかったか
  • 境界でバリデーションできなかったか
  • テストで検出できなかったか
  • ログが足りなかったか
  • 責務が混ざっていなかったか

ただし、すべてのバグで設計を変える必要はありません。
小さな修正と回帰テストで十分な場合もあります。
同じ種類のバグが入り込みやすい形になっているなら、設計も見直します。

始め方

npx skills@latest add mattpocock/skills

インストール時に

/setup-matt-pocock-skills
を選んで実行すると、issue tracker、triage labels、
CONTEXT.md
や ADR を置く場所を記録できます。他のスキルは、その記録を共通語彙として使えます。

このセットアップは

/tdd
/diagnose
には必須ではありません。soft dependency として設計されているため、記録なしでも動きます。

次に小さな機能を足すときは

/tdd
から始める。
すでに壊れているバグを追うときは
/diagnose
から始める。

エージェントに「考えて直して」と渡すのではなく、
「この判定基準を満たすように進めて」と渡す。

それだけでも、推測と変更のループに確認ステップを入れられます。