リファクタリングが「トークン代」を減らす、という新しい経済合理性
AIエージェントにコードを書かせる時代になって、リファクタリングの価値の測り方がひとつ増えたかもしれない。読みやすさや保守性といった従来の理由に加えて、「エージェントが次に修正するときに消費するトークンが減る」という、金額で示せる効果である。
Thoughtworks の CTO である Giles Edwards-Alexander が、この効果を実験で数値化した記事を公開している(Martin Fowler のサイト内、The economic benefit of refactoring)。エージェント開発を日常的に回しているエンジニアやテックリードにとって、リファクタリングの投資対効果を上司や自分に説明する材料として使える内容だ。ざっくり言えば、15段階のリファクタリングを経て、同じ機能追加にかかる入力トークンが83%減った、という話になる。
発端は1.7万行に膨れた1ファイル
著者は15万行規模のアプリケーションを、ほぼすべて AI エージェント(主に Claude Code と Cursor)で構築した。Web UI、機械学習、バックグラウンドジョブ、自動デプロイまで含む構成で、うち約12万行が Rust、残りが TypeScript と Terraform だという。
問題が起きたのはデータアクセス層だった。機能を足すたびにエージェントがコードを書き加え、ひとつの Rust ファイルが17,155行にまで膨張していた。同じ HTTP リクエストの組み立て、同じ JSON のエンコード・デコードが、データベース操作のたびに繰り返し書かれている。人間が書いても起こりうる腐敗だが、エージェントは「既存の書き方に倣って追記する」ため、重複が加速しやすい。
このファイルが大きいと何が困るか。エージェントが小さな機能を1つ足すためだけに、巨大な1ファイルをまるごと読み込む必要が出てくる。読む量がそのまま入力トークンになり、API 料金と実行時間に跳ね返る。
「前後で同じ作業をやらせて測る」という実験設計
著者は効果を厳密に測るため、次のような手順を組んだ。まず Martin Fowler 流のリファクタリング計画を立てる。次に代表的な機能追加として「ItemWatchStore という trait を3つのメソッドつきで追加する」という作業を用意する。そして、リファクタリングを1ステップ適用するたびに、この同じ機能追加を最初からやり直させ、入力トークン・出力トークン・実行時間・行数を記録していく。
ここで巧妙なのは、毎回まっさらな(fresh)エージェントを使っている点だ。同じエージェントに続けてやらせると「学習」して2回目以降が有利になってしまうため、その偏りを排除している。トークン数は tiktoken で文字数を4で割る近似として数えたとのことだ。
15段階で何をしたか
適用したのは Fowler のパターンに沿った地道な分割・抽出の連続だ。おおまかには次のような流れになる。
- HTTP 通信を担う
FirestoreClientをドメインのクエリ処理から切り離す - 繰り返し登場するヘルパー関数(ドキュメント ID の抽出など)を共通化する
- 15回以上使い回されていた絞り込みロジックを述語として集約する
- 128回以上呼ばれていた JSON 生成マクロを関数に置き換える
- 32個のクエリ定数を
queries.rsに、17個の trait 定義をtraits.rsに移す - trait をドメイン別(計画・コンテンツ・人・システム)の4ファイルに分割する
- エンコーダ/デコーダを
codec.rsに、テスト用のFakeStoreをfake_store.rsに移す - 巨大な
FirestoreStoreを trait ごとに10ファイル(各120〜650行)へ分解する - テストをソースの隣に置き直し、最後に
store/配下を整理する
要するに、1枚岩を意味のある単位に切り分けていく作業だ。目新しい魔法はなく、教科書どおりのリファクタリングである。
入力トークン83%減、しかも出力はほぼ横ばい
結果は明快だった。同じ機能追加にかかる入力トークンが、ベースラインの159,564から最終的に27,360まで下がった。差し引き132,204トークン、率にして83%の削減である。最大ファイルは17,155行から3,695行へ(約78%減)。一方でデータ層全体の行数は17,155から16,608とほぼ維持されている。コード総量を減らしたのではなく、配置を変えただけでこの差が出たわけだ。
Sonnet 5 の入力価格(100万トークンあたり3ドル)で換算すると、1回の変更あたり約39.7セントの節約になる。金額としては小さいが、データアクセス層に触れる将来の変更すべてに効いてくる、というのが著者の主張の肝だ。
興味深いのは出力トークンが1,705から2,113へと微増(+24%)にとどまり、ほぼ横ばいだった点である。リファクタリングが減らすのは「読む複雑さ」であって「書く複雑さ」ではない、という示唆だ。エージェントが生成するコード量そのものは、整理しても大きくは変わらない。
削減が最も効いたのはファイルを分割するステップで、単に関数を抽出しただけの初期段階ではあまり効いていない。エージェントが「関係するファイルだけを、より小さく絞り込めるようになる」ことが節約の源泉であって、コードを綺麗にすること自体が直接効くわけではない、という理解ができる。
エージェントは自分ではリファクタリングできなかった
もうひとつ現場の実感として重要なのは、著者が「Claude はリファクタリングが得意ではなかった」と率直に書いていることだ。どこをどう直すべきかをエージェントが自律的に見つけることはできず、人間の明示的な指示が要る。機械的な適用も雑で、sed や grep のスクリプトはインデント処理でつまずいた。最も効果の大きかった store の分割は、最初は見落とされて後からやり直したという。
つまり、リファクタリングの判断と設計は依然として人間側の仕事だ、ということでもある。実験全体は約8時間(多くは無人で放置)、トークン消費の上限見積もりは約500万トークンとされる。この前準備コストと、将来の変更で回収できる節約分を天秤にかける、という構図になる。
どう受け止めるか
ここからは私見だが、この記事の価値は「83%」という数字そのものよりも、リファクタリングの効用を客観的に測るための実験フレームを示したことにあると思う。前後で同一タスクを fresh エージェントに反復させてトークンを測る、という方法は、自分のリポジトリでも真似できる。技術的負債を「返済すべき」と感情論で言う代わりに、「この層に月に何回変更が入り、1回あたり何セント節約できるか」という言葉で語れるようになる。
ただし限界も著者自身が認めている。今回の機能追加は小さく単純なもので、大規模な構造変更や、より複雑な機能では出力トークンの挙動も変わりうる。単発の1データ層での実験でもある。過度な一般化は禁物で、あくまで探索的な第一歩という位置づけだ。それでも、AI エージェント時代に「なぜリファクタリングするのか」の答えがひとつ増えたことは確かだろう。
出典: Giles Edwards-Alexander「The economic benefit of refactoring」(ニュースレター『Leadership in Tech』で紹介)。原文: https://martinfowler.com/articles/exploring-gen-ai/refactoring-economic-benefit.html