Uberはコード消去AIに何を任せなかったのか
フラグ1万6,000個を整理したAutoClean、エージェントのそばに置いたツール・権限・審判の分業表
AI・テックUberが公開した「コードを消すAI」
Uberのエンジニアリングブログに、AutoCleanというシステムを紹介する記事が掲載されました。使い終わって残ったフィーチャーフラグ1を見つけ出し、関連コードを削除し、壊れたテストを修正したうえで、検証まで済ませたdiff2を担当チームに送るシステムです。Uberは過去数年間でこのシステムを使い、フラグ1万6,000個以上、コード100万行以上を整理したと明らかにしました。
AIがコードを書いてくれるという話は、もう珍しくありません。この記事の方向はその逆です。すでに動いているプロダクションコードをAIが消すのです。ところが記事を最後まで読んでみると、エージェントが何を成し遂げたかと同じくらい、Uberがエージェントに何を任せなかったかが詳しく書かれています。今日は、その分業の形をたどってみます。
フラグ1つを消すのに、なぜ判断が必要なのか
フィーチャーフラグは、コードの中に置いたスイッチです。新機能を一部のユーザーにだけ試験的に公開したり、問題が起きたときに再デプロイせずに止めたりできるようにしてくれます。Uberはモバイルアプリとバックエンドサービス全体で、数千個のフラグを管理しているそうです。
問題は、機能を全ユーザーに向けて有効化した後です。スイッチは常にオンのままなのに、そのスイッチを取り巻く条件分岐や旧経路、その経路を検査していたテストはそのまま残ります。チームは次の機能へと移り、整理作業は新しい製品開発と優先順位を争って後回しにされます。Uberの規模では、こうして後回しにされた小さな整理が、あっという間に大きな積み残しになるといいます。
Uberが挙げる例は、注文処理のコードです。設定からnew_order_pipelineフラグの値を読み取ってcfg.UseNewPipelineに格納し、その値をprocessOrder関数に引数として渡し、関数の中で新パイプラインと旧パイプラインのどちらかを選ぶという構造です。このフラグが完全にロールアウトされ、値が常にtrueになったとしましょう。
フラグを読み取る箇所にtrueを入れ込むこと自体は、機械的な作業です。判断が必要になるのは、そこから先です。
processOrderのuseNewPipeline引数が本当に常にtrueなのかを確認する必要があります。例では呼び出し元が1箇所だけですが、実際のコードでは他の箇所が別の値を渡している場合があります。すべての呼び出し箇所を見終えて初めて、引数を消せるようになります。- 引数を消して値を関数の中に押し込んで初めて、旧パイプラインの分岐がデッドコードであることが明らかになります。関数だけを単独で見ると、両方の分岐がまだ生きているように見えてしまうからです。
- 設定構造体の
UseNewPipelineフィールドも消せるかもしれません。ただしそれは、他のコードがそのフィールドを読み取ったり、構造体の形に依存したりしていない場合に限られます。
フラグの値は、設定の初期化から出発し、構造体フィールド、呼び出し箇所、関数引数を経てデッドコードへとたどり着きます。Uberの整理によれば、定数を埋め込むような決定論的な書き換えは静的ツールが得意とする一方、AIが最も役立つのは、この伝播経路をたどりながらどこまで消してよいかを判断する作業だといいます。テストも同じ問題を抱えています。消してよいテストもあれば、新しいデフォルトの動作に合わせて書き直す必要のあるテストもあります。
Uberは、この負債が人間だけを苦しめているわけではないとも書いています。AIエージェントもデッドコードを読み解くのに時間を費やし、その分だけセッションが長く、コストのかかるものになるというのです。ただし、どれくらい長くなるかについての具体的な数値は、記事には書かれていません。
ブール演算はツールに任せました
AutoCleanは、Uberのフィーチャーフラグ管理システムで古くなったフラグを見つけ、3段階で処理します。コードを書き換え、CI3で壊れた箇所を直し、結果を検証します。検証を通過したdiffは担当チームに送り、人間が最終判断を下すまでdiffが古びないよう更新し続けます。
最初の段階のエージェントはコードベース全体を見ることができ、ファイル検索・コード編集・条件式簡略化ツールを使えます。注目すべきは最後のツールです。Uberは、ブール式や条件文を整理する作業をエージェントに直接やらせることはしませんでした。自社のコード変換エンジンであるPolyglotPiranhaをMCP4で接続し、エージェントが必要なときに呼び出して使えるようにしたのです。エージェントはフラグの位置にtrueを入れ、PolyglotPiranhaで式を単純化し、値をprocessOrderまで追跡して引数と死んだ分岐を削除し、これ以上整理するものがなくなるまでこの過程を繰り返します。
理由も明記されています。条件式を単純化する際に小さなミスをすると、プロダクションで実行される分岐が変わってしまいます。コンパイルも通り、見た目は問題なさそうなのに、動作だけが変わってしまう類のミスです。Uberは、エージェントシステムだからといってすべての段階をエージェントが担う必要はなく、誤った場合に動作を静かに変えてしまいかねない変換ほど決定論的に処理したいと書いています。同じ入力に対して常に同じ結果を出す方式です。
Piranhaは新しいツールではありません。Uberは2020年3月にPiranhaをオープンソースとして公開した際、それまでに約2,000個の古いフラグと関連コードを削除したと明らかにしました。当時もすでに、毎週フラグ管理システムから古いフラグを抽出してdiffを作成し、フラグを最初に作った人をデフォルトのレビュアーに指定するパイプラインが稼働していました。AutoCleanは、この決定論的なツールを捨てるのではなく、判断が必要な部分にだけエージェントを組み込んだ構造として読み取れます。
CIで壊れたときも順序は同じです。使われなくなったデフォルト値を削除したり、リントエラーを整理したりするような機械的な失敗は、安価な決定論的修正ツールが先に引き受けます。修正ツールが認識できなかった失敗だけが次に回され、テストを削除するか書き直すかといった判断が必要な場合に初めて、エージェント修正ツールが引き受けます。修正のたびにCIを再実行し、グリーンになるか、定められた試行回数を使い切るまで繰り返します。
テスト修正エージェントに対しては、テストファイルしか開けないようにしました
この段階でUberが書き残した一文があります。修正エージェントごとに手を加えられる範囲を狭く定めていて、例えばテスト修正エージェントはテストファイルしか直せないということです。そう制限しなければ、エージェントは失敗しているテストを通すために、その下にあるプロダクションコードを書き換えてしまうことがあると言います。 Uberがまさに防ごうとした結果です。
この部分が怖いのは、その行動がエージェントの立場からすればかなり合理的だということです。目の前にあった信号は「テストを通せ」であり、プロダクションコードを書き換えればテストは通ります。コードを削除せよという指示が、コードを書き換える行為へとすり替わるわけです。Uberが文章に記していた対応は、指示文ではなく権限でした。直せるファイルそのものを制限したのです。
青信号の先に立てた審判者たち
CIが青信号ならそれで終わりでしょうか。Uberはそうは考えていません。ビルドとテストを通過しても、誤った整理はありえます。エージェントがテストを修正する代わりに削除してしまい、そのテストが残すべき挙動を検査していた、ということもあり得ます。フラグの値を差し替えただけで、周辺コードにまで単純化を波及させられずに途中で止まってしまうこともあります。どちらもCIが必ずしも捕捉できるとは限らない失敗です。
そこでUberは検証を二層にしました。まず静的検査が、ルールとして書き下せる問題をふるいにかけます。常にtrueしか返さなくなった関数、本来単純化されるべきだった条件文、古いフラグ経路からしか到達しない死にコードといったものです。これを通過すると、CodeCourtというエージェント検証層に進みます。
CodeCourtは整理diffが満たすべき性質を一つずつ定義し、性質ごとにその一点だけを確認するレビューエージェントを割り当てます。記事に例として挙がっている性質は四つです。
| 性質 | 確認する内容 |
|---|---|
| 追加なし | 整理はコードを削除・単純化するだけで、新しいコードを入れない |
| 動作保存 | 整理の前後でプログラムの観察可能な挙動が同じであること |
| 意図からの逸脱なし | 依頼されたフラグのみを扱う。関連して見えても他のフラグは削除しない |
| エージェントの痕跡なし | エージェントが作業中に残したコメントや副産物がないこと |
レビューエージェントたちは同時に動き、判定はシンプルです。すべて通過すれば人間によるレビューに回り、一つでも失敗すればそのdiffは人間が目にする前に破棄されます。
Uberはこう分割した理由も記しています。整理エージェントは長い指示を抱えて複数の段階をこなしますが、指示が増えるほど一部は埋もれ、一部は実際に互いにぶつかり合います。すると、エージェントはCI通過のような最も近い信号に合わせて動き、肝心の整理条件を破りやすくなるというのです。そこで、すべての条件を一つのエージェントに記憶させる代わりに、最終diffが満たすべき性質として切り分けました。初期には一つのエージェントにdiff全体を最初から最後まで検討させてみたものの、性質ごとに担当するレビューエージェントを複数置くほうが結果は良かったそうです。
Uberの説明によれば、人間のレビュアーの役割も変わります。整理の過程で挙動が変わっていないかを一行ずつ洗い出す作業は前段に任せ、「このフラグを今削除してよいか」という事業判断に集中できるというわけです。
1万6,000個という数字の読み方
文章の最後に出てくる数字は大きく見えます。過去数年間でフラグ1万6,000個以上、コード100万行以上を整理したというのです。この数字を読むときは、二つのことを同時に見る必要があります。
まず期間です。Uberは「過去数年間」としか書いていません。この累計がいつから始まったものなのか、エージェントが導入される前のPiranhaパイプライン時代の分がここに含まれているのかは、文章だけでは分かりません。2020年3月の時点ですでに約2,000個を削除済みだったことを考えると、1万6,000個すべてをエージェントの成果として読むのは難しいでしょう。
次に欠けている数字です。いくつのdiffがCodeCourtで却下されたのか、人間のレビュアーが何件を承認し何件を差し戻したのか、diff一件にどれだけの時間とコストがかかるのかは文章にありません。「一つでも失敗したら破棄する」という設計がどの程度の頻度で作動するのかも分かりません。
比較できる数字は別の会社にあります。2026年9月のInfoQの報道によると、DoorDashは約623のリポジトリにまたがる6万個以上のフラグを整理するためにマルチエージェントシステムを構築し、古いフラグ50個で評価しました。45個から使えるPRが生成され、整理一件あたり平均13.8分と4.79ドルがかかりました。人間が行えば1〜2時間かかるというDoorDash自身の見積もりと並べた数値です。単純なフラグは一度で100%整理できましたが、複雑なフラグは85%で、エンジニアが5回介入しました。介入が必要だったのは、深い呼び出しチェーンとインターフェースをまたぐ引数の受け渡しの部分でした。DoorDashはコードを修正する前に、エンジニアに目標値をまず確認させるようにしていました。
両社の数字で優劣をつけることはできません。言語も、コード構造も、測定基準も違います。ただ、DoorDashがつまずいた箇所は、Uberの文章が指摘していた箇所と重なります。値を書き込む側よりも、その値が複数のファイルを越えてどこまで広がっているかを追跡する側でした。
Oswarldの視点
私がいつも言っていることがあります。すべての人、すべての仕事に超知能が必要なわけではなく、仕事と環境に合ったモデルが別にあるということです。私もそうやって使い分けています。2026年9月時点で、全体の計画立案とオーケストレーション、初期のブループリントは依然としてClaudeやGPT-6に任せ、時間がかかって反復の多い作業はDeepSeekやGLMに任せています。
Uberの記事でその話に最も近い部分は、ブール演算をPolyglotPiranhaに任せた箇所です。エージェントができる仕事であっても、間違えると静かに動作が変わってしまう計算は、同じ入力に同じ答えを出すツールに任せたわけです。仕事に合ったモデルを選ぶところからさらに一歩進んで、仕事の一部をそもそもモデルの外に出してしまったことになります。
テスト修正役にテストファイルしか開かせなかった判断も、同じ線上にあると見ています。何をやらせるかとともに、何をやらせないかを決めたわけです。エージェントを業務に導入する際、「どの仕事を任せるか」という問いは簡単に出てきます。Uberの記事は「その仕事をする中でどこまで波及しうるか」を同時に問わせてくれます。
ひとつでも失敗したら直さずに捨てるという設計は、人間のレビュアーの時間を節約するために機械の時間をより多く使うという選択として読めます。その選択が割に合うかどうかは、いくつ捨てたのかを知らなければ判断できませんが、その数字が抜けているのは残念なところです。
おわりに
Uberの記事には、エージェントの能力を示す文章と同じくらい、エージェントの周囲に張り巡らせた仕組みを説明する文章が多く含まれています。破棄されたdiffの数やコストが公開されていないため、効果を数字で測るのは難しいです。それでも、どの作業を道具に、どの作業をエージェントに、どの作業を人間に任せたのかは、かなり詳しく書かれています。
💬 読者さんのチームのコードには、オンのまま放置されているフィーチャーフラグがだいたい何個くらいありそうですか? おおよその見当で構わないので、コメントに残してください。
📨 古いコードの整理を先延ばしにしている開発チームの同僚がいれば、この記事を転送してください。
参考資料と関連リンク
- Uber Engineering, 「Using AI to Delete Code: Agentic Tech Debt Cleanup at Uber」。AutoCleanの構造、CodeCourtの特性、1万6,000個・100万行という数字の出典です。
- Uber Engineering, Introducing Piranha: An Open Source Tool to Automatically Delete Stale Code、2020年3月17日。約2,000個のフラグ整理と週次パイプライン、デフォルトレビュアー指定方式の出典です。
- uber/piranha、GitHub。PiranhaおよびPolyglotPiranhaのリポジトリです。
- Leela Kumili, DoorDash Uses Multi Agent LLMs to Clean up 60,000 Feature Flags、InfoQ、2026年9月18日。DoorDashの評価数値の出典です。
📝 用語解説
각주
-
フィーチャーフラグ(feature flag):コードの中に入れておくオン・オフのスイッチです。新機能を一部のユーザーにだけ表示したり、問題が起きたときに再デプロイせずに機能を止めたりできるようにしてくれます。 ↩
-
diff:コードがどう変わったかを示す変更分です。レビュアーはこの変更分を見て反映するかどうかを判断します。 ↩
-
CI(継続的インテグレーション、Continuous Integration):コードが変わるたびに自動でビルドとテストを行い、問題がないかを確認する仕組みです。 ↩
-
MCP(Model Context Protocol):AIモデルが外部のツールやデータに接続して使えるようにする標準的な接続方式です。 ↩

あなたの視点が次の号をつくります
この号で最も共感した点、あるいは違う経験をした点はどこですか。