AIネイティブ開発におけるリファクタリングの判断基準
この記事で分かること
AI(特にClaude、ChatGPT、GitHub Copilotなど)を使ったコード生成が日常化する中で、従来のリファクタリング基準が機能しなくなってきています。
この記事では、AIと協業する開発におけるリファクタリングの新しい判断基準と実践的な対処法を整理します。
前提:AI開発特有の「理解の負債」とは
従来の技術的負債との違い
| 種類 | 性質 | 問題のタイミング |
|---|---|---|
| 技術的負債 | 将来の変更コスト | 後で修正が必要になる |
| 理解の負債 | 現在の理解コスト | 今すぐ理解できないと次に進めない |
AI生成コードで起きる典型的な問題:
- 理解を伴わないコードの蓄積:動くが誰も設計意図を説明できない
- 一貫性の欠如:スタイル・パターンがバラバラ
- 過剰な複雑さ:エッジケース対応で条件分岐が増殖
書くコストは下がったが、読んで理解するコストは上がっている。
一方で、AIが解説してくれる時代
「人間が読めないコード」は本当に問題なのか?
実際、最近は:
- コードを直接読まず、AIに概要を聞く
- 詳細が知りたいときはAIに仕様を聞く
- 人には読みにくくてもAIは正確に解釈できる
問題は「AIに聞けば済む」で回らなくなる瞬間です。
AIがつまずくポイント:デバッグの迷走
よくある失敗パターン
不具合を指摘してAIに修正を依頼すると:
1回目: ログ出力を追加
2回目: 同じ箇所に別のログを追加
3回目: 関係ない箇所にログを追加
4回目: エラーハンドリングを追加(根本解決せず)
5回目: また別のログを追加
...
結果: デバッグコードだらけで問題は未解決
なぜAIはデバッグが苦手なのか
- 状態を持たない:前回試したことを構造的に覚えていない
- 仮説を立てない:網羅的に見えて場当たり的
- 諦め時がわからない:延々と小手先の修正を提案
結論:「なぜ」を掘る作業は、まだ人間がハンドルを握るべき。
新しいリファクタリング基準:「AIが迷走しない設計」
AIにとって扱いやすいコードの条件
従来の「人間のための可読性」から**「AIが正確に扱える形」**へ。
| 条件 | 理由 | 具体例 |
|---|---|---|
| スコープが小さい | 一度に把握できる範囲を超えると精度が落ちる | 関数は50行以内、責務は単一に |
| 依存関係が明確 | 暗黙の結合が多いと影響範囲を見誤る | グローバル変数を避ける、DIを使う |
| 状態が少ない | 状態遷移が複雑だと追跡できない | イミュータブルな設計、純粋関数 |
| テストが充実 | 変更の影響を即座に判定できる | 単体テスト、統合テストのカバレッジ |
これは昔から言われる「良い設計」と重なりますが、理由が変わっています。
人間とAIで最適解は違う?
分割粒度のジレンマ
人間の場合:
- 流れで理解したい(ストーリーとして)
- 分割しすぎると全体像が見えない
AIの場合:
- 関数定義を見に行くコストがゼロ
- 細かく分割されている方が扱いやすい
実践的な落とし所
現時点では大きく矛盾していないため、両方を満たす設計を目指すのが現実的:
// NG: 一つの関数に詰め込みすぎ(人もAIも辛い)
function processUserData(user) {
// 100行以上の処理...
// 状態管理、バリデーション、DB更新、通知が混在
}
// OK: 責務ごとに分割(人もAIも理解しやすい)
function processUserData(user) {
const validated = validateUser(user);
const updated = updateUserInDB(validated);
notifyUserUpdate(updated);
return updated;
}
実践的な対処法
1. AIに渡すスコープを絞る
デバッグで迷走するのは、範囲が広すぎることが原因。
❌ 「このファイル全部見て不具合を直して」
⭕ 「この関数の入出力を検証して。期待値はXXX」
2. 仮説は人間が立てる
❌ 「エラーの原因を調べて」
⭕ 「認証部分が怪しい。この前提で調べて」
3. 3回ルール
同じアプローチで3回失敗したら戦略を変える:
- セッションをリセット
- 「違う観点で原因を考えて」と明示的に指示
- または人間が直接デバッグ
4. 本体を直接いじらせない
# 検証用ブランチで試させる
git checkout -b debug/issue-123
# 再現用テストを書かせる
"この問題を再現するテストケースを書いて"
# 原因特定後、本体への反映は人間が判断
5. 生成時にテストもセット
❌ 「この機能を実装して」
⭕ 「この機能を実装して。テストも一緒に書いて」
リファクタリングのタイミング
やるべきサイン
- AIがデバッグで3回以上迷走した
- 新機能追加時に既存コードの影響範囲が読めない
- 同じバグが繰り返し発生する
- AIの生成コードに一貫性がない
具体的な進め方
## Phase 1: 現状把握(1日)
- AIが詰まる箇所をリストアップ
- 複雑度が高い関数を特定(循環的複雑度 > 10)
## Phase 2: スコープ分割(2-3日)
- 大きな関数を責務ごとに分割
- グローバル状態を局所化
## Phase 3: テスト追加(2-3日)
- リファクタリング対象にテストを追加
- カバレッジ目標: 70%以上
## Phase 4: 段階的リファクタリング(1週間)
- 1日1ファイル程度のペース
- 機能追加のPRとは分ける
チェックリスト
リファクタリング後の確認:
### AIの扱いやすさ
- [ ] 関数は50行以内に収まっているか
- [ ] 1関数1責務になっているか
- [ ] グローバル変数を使っていないか
- [ ] 副作用が明示的か
### 人間の理解しやすさ
- [ ] 命名が意図を表しているか
- [ ] ファイルの責務が明確か
- [ ] 重要な判断にコメントがあるか
### 保守性
- [ ] テストがあるか
- [ ] AIに説明させて正確に解釈できるか
- [ ] デバッグで迷走しないか
まとめ
現時点での答え
「AIがデバッグで迷走しない粒度」を指標にする
具体的には:
- 関数を小さく保つ(AIが一度に把握できる範囲)
- 状態管理を明確にする(暗黙の依存を減らす)
- テストを書く(AIの変更を検証できる状態)
今後の変化
- AIの世代が変われば最適解も変わる可能性
- でも変更コストはAIが下げてくれるはず
- 「誰のための可読性か」を問い続ける必要がある
実践のヒント
- まずは困っている箇所から着手
- 完璧を目指さない(ボーイスカウトルール)
- AIとの分業を意識する(生成はAI、判断は人間)
参考
- リファクタリングの基本: Martin Fowler - Refactoring
- 循環的複雑度の測定:
radon cc(Python),eslint-plugin-complexity(JavaScript)
こうした判断や内省のプロセスを、
エンジニア向けにもう少し整理して書いています。
興味があれば、こちらにまとめています。
https://tielec.blog/