TDD(Test-Driven Development)とLLMの組み合わせ
- TDDは、プログラムの作成を始める前に、包括的な単体テストを先に作成する開発方法論
- テストが事実上の仕様書の役割を担うため、最終的にすべてのテストが通れば、コードの正しさをある程度証明できる
- 従来、TDDは生産性を損なう、あるいは非効率だという批判を受けることもあった
- しかしLLMの登場により、テスト作成とコードの反復修正のプロセスがはるかに容易になった
私が普段LLMを使う方法
- GitHub Copilotのようなツールを積極的に使ってきた
- LLMは反復パターンを見つけて次の数行を自動補完するのは得意だが、問題全体を深く理解して、完成度の高いモジュールを一度で作り上げるのはしばしば難しい
- 問題解決に必要な文脈を過剰に与えると、モデルが話題から逸れやすい
- 必要に応じて情報(エラー出力など)を部分的にだけ提供して作業を進めると、モデルはデバッグでも非常に役立つ
- IDE、ターミナル、チャットインターフェースの間でコピー&ペーストを繰り返す過程に摩擦があることを実感している
自動化できるだろうか?
- このプロセスを自動化するために、独自に event loop の概念を導入した
- 最初のプロンプトに、実装する関数の仕様と関数シグネチャを明記すると、モデルは単体テストとコードのドラフトを提示する
- このコードを
sandbox ディレクトリに保存し、自動で go test を実行する
- テストが失敗した場合は、2回目以降の反復プロンプトに既存コードとテスト結果(コンパイルエラーや失敗情報)をあわせて送信する
- モデルはそれをもとに、修正したテストと実装コードを再提案する
- すべてのテストが通るまでこのプロセスを繰り返す
- このアプローチは、文脈を過剰に蓄積しなくても段階的な改善を可能にする
- モデルが同じテストケースで繰り返し失敗することもあるが、その場合は人間が問題箇所を直接指摘してヒントを与える
- LLMが作成したテストが十分に厳密かどうかを疑うべき「監視者不在」の問題を認識する必要がある
- 同じ誤りや不完全な設計を、コードとテストが一緒に共有してしまう可能性がある
- したがって、人間が追加のテストケースで補強するプロセスが重要になる
- 必要であれば、mutation testing のような手法をAIで試してみることもできる
LLMベース開発と認知負荷(cognitive load)
- LLMとともにTDDを適用すれば、一般的なアルゴリズム問題だけでなく、実際の依存関係を持つコードベースでも可能だと見込んでいる
- ただし、プロジェクト構造はより小さな単位に分割して保守性を高め、各ディレクトリ/パッケージが独立してテスト可能であるべき
- 各パッケージは、主要な型定義(
shared.go)と特定ロジックを担当するファイル(x.go)、およびテスト(x_test.go)に分けて、認知負荷を下げる方法を推奨する
- AIを活用する過程では、毎回コード全体をモデルに渡す代わりに、特定の部分だけを選択的に含めて、モデルが集中できるようにする
- これはテストカバレッジを高めつつ、モジュール間の結合度を下げ、長期保守にも利点をもたらす
- 大きなプロジェクトでも、小さく明確な単位に分割し、各単位にロジックを十分に盛り込みつつも、スコープは最小限に抑える構造を志向する
まとめ
- AIの進化速度を考えれば、明日にでも新しいアーキテクチャが登場し、LLMの限界を超えるかもしれない
- したがって、10万行を超える大規模なレガシーコードを急にリファクタリングするよりも、小さな規模からTDDとLLMの組み合わせの可能性を探ってみることを勧める
- TDDとLLMの融合は、コード自動生成とテスト品質管理の両方に前向きな変化をもたらすと期待できる
5件のコメント
ほかの開発専用AIサービスは、どんなパイプラインを使っているのだろうかとつくづく考えてしまいます。
(こういうのを見ていると)そのうち、電脳のために脳へ電気刺激用の電線を挿入するようになる気がします。
テストコードを入れるのは良いことだとは思いますが、この人が作ったプログラムにはメリットがないように思います。
clineやaiderでもコマンドラインを実行して結果を受け取ることができるので、そのプログラムではプロンプトだけうまく使うほうが、ほかの利便性を考えるとよい気がします
Builder.ioが作ったマイクロエージェントも似たようなアプローチです。 https://github.com/BuilderIO/micro-agent 私もLLMとTDDを何度も試してみましたが、デザインシステムなどによる抽象化もうまくやる必要がありますし、コンベンションやパターンもしっかり整備されている必要があります。テストケースはたいてい自分で書くほうです。(人間の言葉ででも?)何より、この文章でも述べられているように、結合度が低く凝集度の高いモジュールをうまく設計してこそ、限られたコンテキストウィンドウに文脈を押し込めることができるのだと感じました。
LLMは小さな範囲のコードを見るのは得意ですが、全体的な設計や大きな視点についてはまだ物足りない面があるので、
TDDと組み合わせて少しずつ改善していくやり方は良さそうですね。