既存プロダクトに Loop Engineering をそのまま持ち込むと技術的負債が高速で積み上がる — まずリファクタリングで判定基盤を立て直す
前記事では、Loop Engineering はソロプレナー的な新規プロダクトで最も効く、という話を書きました。ゼロから立ち上げて全設計を自分が把握しているリポジトリなら、仕様の権威も検証基盤の設計も意思決定の閉鎖性もそろっているので、Loop Engineering の設計思想がそのまま活きる、という整理でした。
今回はその裏側です。すでに動いている既存プロダクトに Loop Engineering をそのまま持ち込むとどうなるか。結論から書くと、通ってしまうゲートの下で技術的負債が高速で積み上がります。しかもゲートは通っているので、負債が積み上がっていること自体に気づけません。
これはエージェントが悪いのでも Loop Engineering の設計が悪いのでもなく、判定基盤の側が Loop Engineering の要求水準に達していないだけです。だから対策も明快で、Loop Engineering を回す前に、判定基盤の底上げ、つまりリファクタリングを先にやる、というだけの話になります。
この記事では、なぜ既存プロダクトでは加速度的に負債が増えるのか、どういうリファクタリングが「Loop Engineering の判定基盤」になるのか、そしていつ Loop Engineering を有効化してよいのかを分解します。
なぜ既存プロダクトで負債が高速で積み上がるのか
Loop Engineering はゲートの通過を「クリーン」の唯一の指標として使います。5 つのゲート(前々回の記事)を通れば、PR は review-ready として人間に渡され、マージ判断だけが残ります。この設計は、ゲートが判定できる範囲がリポジトリの品質基準と一致していることを前提にしています。
既存プロダクトでは、この前提がまず崩れます。しかも壊れ方は「ゲートが厳しすぎて通らない」ではなく、「ゲートが甘くて何でも通る」です。次のような形で現れます。
テストが落ちないので通る
10 年動いているコードベースでは、テストが「一応存在するが実際にはリグレッションを検出しない」状態になっていることがよくあります。カバレッジ数値上はそこそこあるのに、実装を大きく壊してもテストが緑のまま、というやつです。
Loop Engineering はテストの緑をリグレッションなしの証拠として使います。だからテストが判定できないリグレッションはすべて通り抜けます。人間なら「この変更なら手動でここを確認しておこう」と勘で補うところを、エージェントは補いません。テストがカバーしていない領域は、存在しない領域と同じ扱いになります。
問題は、エージェントの速度でこの通り抜けが起きることです。たとえば PR 数が 1 日 3 本から 15 本に増えたと仮定すると、1 本あたりの負債の増分が同じなら、5 倍の速度で積み上がります。これは速度と負債の関係を説明する例であり、実測値ではありません。
型が緩いので通る
TypeScript の any、Python の Any や型無しのままの関数、Ruby のダックタイピングに任せた境界。既存プロダクトには、型による保証が実質ゼロの領域が必ずあります。
Loop Engineering は型チェックを判定基盤の 1 つとして使いますが、型が緩い領域では判定していません。判定していない領域では、エージェントは「ここは自由に触ってよい場所」と解釈します。人間なら「ここは触るとまずいから最小限にしておこう」と自己制限する場所を、エージェントは自己制限しません。
そして、型が緩い領域を触った変更は、型が厚い領域を触った変更と同じ 5 つのゲートを通ります。ゲート側からは区別がつきません。
境界が曖昧なので、触ってはいけない場所まで触られて通る
これがいちばんたちが悪い形の負債です。
既存プロダクトには、「触ってはいけないが、それがコードから読み取れない」領域があります。歴史的経緯で残っている互換レイヤ、外部システムとの契約が暗黙になっているモジュール、ドキュメントに書かれていない副作用に依存している呼び出し元、といったものです。
Loop Engineering は issue のスコープを唯一の権威として扱いますが、issue に「XYZ モジュールは触らない」と書いていないと、エージェントはそこを触ります。「変更したら他の場所が壊れるはずのコード」がテストで守られていない状態だと、変更しても緑のまま通り抜けます。
しばらくすると、暗黙の契約が破られたまま動いているコードベースができあがります。動いているように見えるので、破られたことに気づくのは、その契約が実際に必要になったとき(例えば別の環境に移したとき、外部連携先が仕様変更したとき、パフォーマンスの臨界を超えたとき)です。それは多くの場合、Loop Engineering を回した本人が忘れた頃です。
変更が増えるほど、検出されない不具合も増える
上の 3 つに共通しているのは、負債が積み上がるメカニズム自体は既存プロダクトが元々抱えているもので、Loop Engineering はそれを新規に生み出しているわけではない、という点です。
Loop Engineering がやっているのは、単に速度を上げることだけです。ただ、速度が上がるということは、同じ品質基準のままだと、単位時間あたりに積み上がる負債の量も同じ倍率で増えるということです。人間の目が届く速度で緩やかに増えていた負債が、届かない速度で高速に増えます。
何をリファクタリングすればいいのか
対策は、判定基盤の側を Loop Engineering の要求水準まで引き上げることです。順序があります。上から順にやります。
1. まず、リグレッションを検出するテストを敷く
いちばん先に来るのはテストです。カバレッジ数値ではなく、「意図せず壊したときに実際に落ちるか」で見ます。
具体的にやることは次の 3 つです。
- 現状のカバレッジのうち「実際にリグレッションを検出できる」割合を数える。実装を意図的に壊して落ちるかを試すのが最も速い
- 主要な業務ロジック・API 境界・データ整形の各所に、値の意味を検証する(
toBeTruthy()ではなく期待値を書く)テストを追加する - 落ちない箇所は、テストを直すのではなく、テストを追加するか、テスト対象のコードを分解して検証可能な単位に切る
この段階で気づくのは、「テストがない」よりも「テストはあるが検証していない」の方が圧倒的に多い、ということです。緩いモックだけで通っているテスト、期待値がなくメソッド呼び出しの記録だけを見ているテスト、E2E だけあってユニットテストが実質ゼロの層。これらは Loop Engineering から見ると存在しないのと同じです。
2. 次に、型で境界を厚くする
テストがある程度整ったら、型で守る領域を広げます。テストは「壊れたときに落ちる」ですが、型は「そもそも壊した書き方ができない」です。エージェントに書かせる場合、後者の方が効率が良いことが多いです。
- 公開 API の入力・出力に厳密な型を付ける
- 内部のデータ構造で、
unknownやanyを通しているものを一段ずつ削る - discriminated union など、状態の網羅を型に押し付けられるパターンを導入する
- コアなドメインモデルは、プリミティブ(
string・number)ではなく nominal 型で表現する
型で守れる領域が広がると、Loop Engineering の 5 つのゲートのうち、Codex /review と Claude Code /code-review の 2 つが判定できる範囲が一気に広がります。ゲートが独立に判定できる領域が広がるほど、レビューが「同じ差分を別々の角度から見た」情報として意味を持ちます。
3. アーキテクチャを Loop Engineering が触りやすい単位に切る
テストと型が整ったら、次はモジュール境界です。ここでやるのは、issue で「触ってよい範囲」を宣言可能な単位にコードを再編成することです。
- 責務が混在した巨大モジュールを、ドメインごとに切る
- 呼び出し関係を有向で保ち、循環参照を消す
- 副作用(DB アクセス・外部 API・ファイル I/O)を境界に寄せて、コアロジックが純粋に近づくようにする
- 「触ると他が壊れる」ではなく「触ってよい単位が明示されている」構造にする
このリファクタリングが効くのは、issue のスコープ宣言と実装の触る範囲が、モジュール境界と一致するようになるからです。「Cart モジュールだけを触る」と issue に書けば、エージェントも人間もその範囲を機械的に判定できます。境界がないまま「Cart 周辺だけ触る」と書いても、周辺の定義が人によってずれます。
順序として、なぜテスト・型・アーキテクチャの順なのかというと、後ろに行くほど大きな変更が必要になり、前がないと後ろの変更が壊れたかを検証できないからです。テストなしで型を厚くするとリグレッションが検出できません。型なしでアーキテクチャを切り直すと、変更の影響範囲が読めません。
4. AGENTS.md・CLAUDE.md にリポジトリの現在地を書く
もう 1 つ、リファクタリングの過程で並行して整えるのが AGENTS.md と CLAUDE.md です。ここには、リファクタリングの結果として得られた「今のこのリポジトリで何が判定基盤になっていて、何がまだ弱いか」を書きます。
- どのディレクトリ・モジュールは Loop Engineering で回してよいか
- どの領域はまだ判定基盤が弱いので、人間のレビューを差し込むこと
- 触ってはいけないファイル、暗黙の契約を持つモジュールの一覧
- 検証コマンドの正確な呼び出し方
- どのテスト種別が「落ちたら本当に問題」で、どれが flaky として扱われているか
これは新規プロダクトの場合よりずっと重要です。既存プロダクトには「暗黙に守られている契約」が必ずあり、それを言語化せずに Loop Engineering を回すと、暗黙のまま破られます。
部分的に有効化するのが現実解
理想を言えば、リポジトリ全体でテスト・型・アーキテクチャが整ってから Loop Engineering を有効化するのが正しい順序です。ただ、既存プロダクトでこれを一気にやるのは現実的でないことがほとんどです。
現実には、リポジトリの中で「先に判定基盤が育った領域」から順に Loop Engineering を有効化していく、という部分適用が正解です。
- テストが厚い・型が厚い・境界が明確なモジュールに対しては、issue のスコープをそのモジュール内に閉じたときだけ、Loop Engineering を回してよい
- そうでないモジュールに触る issue は、Loop Engineering を無効化して、通常の人間ドリブンの開発フローを維持する
- モジュール境界を越える issue は、少なくとも横断部分は人間がレビューする
この判断を毎回頭でやるのは煩雑なので、AGENTS.md か CODEOWNERS のようなファイルに「Loop Engineering 適用可能領域」を明示しておくと運用が楽になります。エージェント側の preflight でこのファイルを読ませ、スコープが適用可能領域を超えていたら止まる、という設計にできます。
部分適用の副次効果として、リファクタリングのモチベーションが具体的になります。「このモジュールを Loop Engineering の対象に入れたい」という目的で判定基盤を整えるので、抽象的な「テストを厚くする」よりも、やるべき作業がはっきりします。
リファクタリング自体に Loop Engineering を使う場合の注意
判定基盤を整えるためのリファクタリングは、判定基盤が整っていない状態で行われます。つまり、Loop Engineering の 5 つのゲートが機能しない状態でリファクタリングをすることになるので、リファクタリング自体の質を Loop Engineering で保証できません。
現実的な折衷案としては、次のような使い方になります。
- 単純な機械的変換(
anyを具体型に置き換える、命名を統一する、ファイルを分割する等)はエージェントに任せてよい。ただし各 PR は人間が全部レビューする - テストを追加するタスクは、まず既存の実装を絶対に変えないという制約下でエージェントに任せる。テストが増えたあとで、初めて実装のリファクタリングをエージェントに任せる
- アーキテクチャの再設計(責務分割・依存方向の反転など)は、少なくとも設計判断は人間がする。エージェントに任せるのは、決まった設計に沿った機械的な移動だけ
このフェーズだけは、Loop Engineering の「判定を全部エージェントに任せる」原則を守れません。判定基盤を作っている最中は、判定は人間の目に依存します。ここを近道しようとすると、判定基盤に「判定基盤が壊れているのに気づかない」バグが埋め込まれます。
Loop Engineering を有効化してよいかの判断基準
最後に、既存プロダクトで Loop Engineering を有効化してよいかを判断する簡単なチェックリストを置きます。全部満たしていなくても部分適用はできますが、以下がすべて満たされていない領域では、無理にループを回さない方が結果的に速いです。
- 対象モジュールのテストを 3 か所意図的に壊して、いずれもテストが実際に落ちる
- 対象モジュールの入出力に、
anyやunknownを通していない厳密な型が付いている - 対象モジュールと他モジュールの境界が、import 関係で追跡可能な形で明示されている
- AGENTS.md か CLAUDE.md に、対象モジュールに関する触ってよい範囲・触ってはいけない範囲・暗黙の契約が書かれている
- issue のスコープを対象モジュールに閉じる形で書ける
- CI がその範囲について、実際に落ちたら人間に届く形で設定されている
この 6 つが満たされたら、その領域に対してだけ Loop Engineering を有効化する。満たされていない領域は、まずリファクタリングで満たしにいく。満たしにいく過程では Loop Engineering は控えめに使う。
以上、既存プロダクトに Loop Engineering をそのまま持ち込むと技術的負債が高速で積み上がる理由と、それを避けるためにテスト・型・アーキテクチャの順で判定基盤を立て直すべきである、という話を、現場からお送りしました。