検索/大きなコードベースで、テスト済みでレビューに出せる変更をAIに作らせる

大きなコードベースで、テスト済みでレビューに出せる変更をAIに作らせる

AIエージェントに大きな多言語のコードベースを直させる時に使う。プロジェクトのラッパーコマンドでビルドし、テストは対象コードのそばに置いて変更前には落ちることを確かめ、レビューの決まりに従い、自分で実行して見た結果だけを報告させる。

このAIルールを使うには
## ビルドとテストは、プロジェクトのラッパーコマンドを通して動かす 大規模なプロジェクトでは通常、コンパイラとテストランナーが1つのプロジェクトコマンドにまとめられており、何かを実行する前にローカルの変更を再コンパイルして反映する仕組みになっている。ビルドと実行は必ずこのラッパー経由で行う。生のテストランナーや以前にコンパイル済みのバイナリを直接呼び出してはいけない。そうした実行は変更を含まない古い成果物を動かすことになり、テストが通っても何の証明にもならないからだ。 どの編集ならネイティブの再ビルドを省略できるかを把握しておく。型宣言や、自動的に再読み込みされるバンドル済みスクリプトなど、コンパイルされない入力だけに触れる変更はフルビルドを必要とせず、無理にビルドしても時間の無駄になる。そうした変更には、代わりに軽量な対象限定チェックを直接実行する。 フルビルドにタイムアウトを設定しない。正当なコールドビルドは長時間かかることがあり、途中で強制終了すると紛らわしく誤解を招く失敗が生じる。ラッパーが末尾の引数をビルド後のプログラムへそのまま渡す仕様の場合、ビルドフラグはそれら実行時引数より前に置き、振り分けが曖昧にならないようにする。 ## テストは、直すコードの既存テストファイルに足す 基本方針として、変更対象のコードに対応する既存のテストファイルにテストを追加する。あるエリアの挙動に関するバグは、新規ファイルではなくそのエリアの既存テストファイルに属するべきものだ。関連するカバレッジのそばにテストを置くことで見つけやすくなり、セットアップの重複も防げる。 リポジトリで確立されたテストの配置構成を踏襲する。サブシステムごとにまとめられたユニットテスト、互換性テスト、プラットフォームおよびWeb APIのテスト、コマンドラインテスト、エンドツーエンドの統合テストは、それぞれ専用のエリアに分かれている。同等のカバレッジがすでに存在する場所にテストを置く。 特殊用途のフォルダは、本来想定されているケースにのみ使う。課題番号をキーにしたリグレッション用フォルダは、真のリグレッション、つまり以前のリリースでは動作していたのに壊れてしまった挙動にのみ使うものであり、その識別子は実在するものでなければならず、仮の番号を使ってはならない。その挙動がそもそも一度も正しく動いたことがないなら、それはリグレッションではなく、テストはそのモジュールの通常のファイルに属する。 ## 何度動かしても同じ結果になるテストを書く 大きな出力に対しては、壊れやすい等価性チェックよりもスナップショットによる検証を優先し、一時パスのような偶発的な値が無用な変動を引き起こさないよう、スナップショットを正規化する。クラッシュや未処理例外の文字列が存在しないことだけを確認するテストは絶対に書かない。そのようなテストはパイプライン上で決して失敗せず、誤った安心感を与えるだけだからだ。 非決定性の原因はすべて取り除く。ネットワークポートをハードコードせず、一時的なポートにバインドして割り当てられたポートを読み戻す。何かを待つためにスリープやタイマーを使ってはいけない。テストしているのは時間の経過ではなく条件そのものだからで、代わりに実際の条件を待機する。一時ディレクトリは手動で作成せずテストハーネスのヘルパーを使い、テスト内でファイル検索やテキスト検索といったシェルユーティリティを使うことも避け、プロジェクト自身のファイルAPIを使う。 テストがプロセスを起動する場合は、終了コードを検証する前に出力を検証する。そうすれば失敗時に単なる非ゼロのステータスではなく有用なメッセージが得られる。 ## テストが変更前は落ちるのを確かめ、実行した結果だけを報告する 変更はテストされるまで完了したことにならない。テストを実行していないなら、そのコードは動かないものとみなす。テストの実行は完了作業の一部であり、任意のフォローアップではない。 新しい各テストが実際に自分の変更を検証していることを証明する。そのテストは変更前のベースラインでは失敗し、自分のビルドでは成功しなければならない。変更を加えなくても通ってしまうテストは何の証明にもならないため、信頼する前に両方向を確認する。 コミットメッセージ、レビューへの返信、状況報告のすべてにおいて謙虚かつ正直であること。自分が成し遂げたことや実際に動作するものを誇張してはいけない。自分自身の実行で観測した結果だけを報告し、まだ検証されていない部分や未完了の部分は、作業が完了しているかのようにほのめかすのではなく、はっきりと述べる。 ## 直す前に、コードベースの作りを調べる 編集する前に、コードベースがどう構成されているかを把握する。どの層がどの責務を持つか、共有ユーティリティがどこにあるか、どのファイルが生成物でどのファイルが手書きかを理解する。生成されたファイルはソース定義から作られるので、ソースを編集してビルドに再生成させる。生成された出力を直接手で編集してはいけない。次のビルドで上書きされてしまうからだ。 自分が触れる挙動を所有しているサブシステムを特定する。パース処理、モジュール解決、ランタイムAPI、ネットワーキング、ストレージ、バンドラー、パッケージマネージャーのいずれであっても、そうすることで変更とそのテストの両方が正しい場所に収まる。 既存のコードスタイルに従う。近隣のファイルを確認して確立されたパターンを把握し、他プロジェクトの慣習を持ち込むのではなくそれに合わせる。手動でのティアダウンよりも、スコープベースの自動クリーンアップのようなその言語固有のリソース管理のイディオムを優先し、ドキュメント化されたエッジケース、例えばリセット時にデストラクタがスキップされる領域に確保された値は先に明示的に解放しなければならない、といった点を尊重する。 ## レビューの決まりを読み、プロジェクトの必須の書き方に従う 成熟したプロジェクトでは、レビュアーがマージをブロックする基準が明文化されたルール集にまとめられている。自明ではない選択をするコードを書く前にそれを読み、互換性レイヤー、ユーザー向けAPI設計、パフォーマンスに敏感なホットパス、プラットフォーム限定コード、依存関係やベンダリングの変更、ドキュメントや型宣言といった、自分が触れる領域固有のガイドがあれば、その具体的な作業に着手する前に読んでおく。 プロジェクトが必須とする規約には正確に従う。自動パイプラインがそれを強制するからだ。定められたブランチ名のプレフィックスを使い、コードコメントは短く保って、より深い説明はドキュメントに移し、それも明示的に求められた時にだけ書く。ファイル操作では絶対パスを使う。プラットフォーム固有の変更を行う場合は、サポートされている全ターゲットでコンパイルする。条件付きでコンパイルされるコードは、対応するターゲットが実際にビルドされない限り型チェックされないからだ。 ## 失敗したビルドは専用の診断コマンドで調べ、ずれていればコマンドを直す 自動ビルドが失敗した時は、失敗しているテストを表示し、ジョブの状況を要約し、全ログを保存するプロジェクト専用の診断コマンドを使う。手作業でその全体像を再構築しようとしてはいけない。これらのコマンドは、他の場所でも失敗している既知の失敗と新規の失敗をすでに切り分け、日常的なノイズも除外してくれる。 変更に対するフィードバックを読む際は、全体像を返すコマンドを使う。一般的な一覧表示はレビューの要約や行単位のコメントを黙って省略することが多いため、トップレベルのコメント、レビュー、インラインコメントを1つの時系列でラベル付けされたビューにまとめて返すツールを優先する。 ヘルパーの出力がおかしいと感じたら、例えば誤ってパースされたアノテーションや、上流の形式が変わったフィールドなど、下流で回避策を講じるのではなく、その表示処理自体を修正する。ヘルパーは背後にあるツールの薄いレイヤーに過ぎず、それを修理しておけば以降の実行はすべて正しくなる。