米Uberのエンジニアリングチームが、社内システムAutoCleanを紹介するブログを公開した。社内のフィーチャーフラグ管理基盤に接続し、安全に廃止できるフラグを洗い出して、関連コードを削除するdiffを自動で生成する。テストを通過すると、エンジニアの承認に回される仕組みだ。ここ数年でこのシステムが整理したのは1万6000件を超えるフィーチャーフラグ、100万行以上のコード。現在もUberの複数のモノレポで稼働を続けている。
記事の署名は、UberのシニアソフトウェアエンジニアであるDaniel Ramos氏とDiego Marcilio氏、ソフトウェアエンジニアのNikhil Parasaram氏。
コード削除が難しい理由
Uberでは新機能をフィーチャーフラグで段階的にリリースしており、基盤上には常に数千件のフラグが存在する。展開が完了しても、条件分岐、補助関数、呼び出し箇所、設定、テストはそのまま残りがちだ。フラグを1つ消すには、これらを同時に修正しつつ、本番の挙動を一切変えないことを保証しなければならない。
ブログは新たなコストにも触れている。デッドコードは人間が読みにくいだけではない。AIコーディングアシスタントもコードベースを解析する際、こうした分岐のために余分な時間とトークンを費やすという。
3段構成のパイプライン
第1段は書き換え。エージェントはコードベースへのアクセス権を得て、MCP経由でUberが自社開発しオープンソース化したコード変換エンジンPolyglotPiranhaを呼び出す。ブール式の簡約や条件の除去はこのエンジンが担当する。著者の理由は明快で、ブール簡約を誤ると「can silently change program behavior」、つまり気付かないうちにプログラムの挙動を変えてしまうため、できるだけ決定論的なツールに任せるという。
第2段はCIの修復。単純な失敗は決定論的な修復ツールが処理する。たとえば不要になったデフォルト値の削除や、lintエラーの解消などだ。複雑なものはエージェントが担当し、あるテストを削除すべきか修正すべきかといった判断がこれにあたる。各修復ツールの権限は厳しく制限されており、テスト修復ツールが触れるのはテストファイルだけ。CIが通るか、試行回数が尽きるまでこのループを繰り返す。
第3段は検証。CIを通過すると、まず静的チェックを実行し、続いてCodeCourtという検証レイヤーに引き渡す。ここでは複数の専門レビュー用エージェントが並列でチェックを行う。ブログが挙げる規則は次のとおり。
- 削除のみ行い、新しいコードは追加しない。
- 観測可能な挙動を変えない。
- 指定されたフラグだけを対象とし、関連する他のフラグを勝手に消さない。
- エージェントの推論過程を示すコメントや痕跡を残さない。
1つでも通らなければ、そのdiffは破棄され、人間の手元には届かない。著者によれば、1つのエージェントにdiff全体を最初から最後まで審査させるより、専門化した複数のサブエージェントに分担させたほうが効果が高いという。
Piranhaからエージェントへ
AutoCleanの土台は、Uberが数年前にオープンソース化したPiranhaだ。このツールはもともと期限切れフラグの削除を目的に作られ、ルールマッチングによる静的な書き換えを行っていた。その後のPolyglotPiranhaでは、ルールエンジンが複数言語に拡張された。今回の変化は周辺部分にある。最もミスの出やすいブール論理は引き続きルールエンジンが担い、ルールでは書ききれない部分、たとえばテストの修正や呼び出し箇所の補完をエージェントが引き受ける。さらに別のエージェント群がその成果を相互にチェックする。
ブログはいくつかの失敗パターンも率直に認めている。エージェントはテストを通すために本番コードを書き換えたり、挙動を残すべきテストを削除したり、早々に作業を切り上げて整理を中途半端に残したりする。指示が多いと、そのうちの1つの制約を見落とすこともある。こうした意味レベルの問題はCIでは検出できず、修復ツールの権限制限やCodeCourtの追加は、まさにこれらに対処するためのものだ。
記事には、成功率、平均所要時間、破棄されたdiffの割合は示されていない。サンプルコードはGoで、他の言語への対応状況も詳しくは触れられていない。著者の説明では、人間のレビュアーが答えるべき問いは今や1つだけで、「このフラグを消してよいか」である。機械的な正しさは、その前の3段で一度ふるいにかけられている。
参考資料:Uber Engineering公式ブログ、CocoLoop、PolyglotPiranha GitHubプロジェクトページ。整理件数はUberのブログが開示した累計値に基づく。