AI(LLM)を活用したコード生成やリファクタリングは開発効率を大きく向上させますが、一方で意図しない仕様変更やデグレードを引き起こすリスクも孕んでいます。本記事では、AIにリファクタリングを依頼する際に「壊れやすいポイント」を整理し、実務で安全に進めるための具体的な手順、プロンプトテンプレート、およびチェックリストを提供します。
読者が抱える課題
- AIにリファクタリングを依頼した結果、シンタックスは正しいがロジックの挙動が変わってしまった。
- 既存のテストコードがないレガシーコードをAIで安全に書き換えたいが、どこから手をつければよいか分からない。
- AIが出力したコードのレビューに時間がかかり、手動で書き換えるのと労力が変わらない。
この記事で分かること
- AIリファクタリングで特にバグが発生しやすいパターン(壊れやすいポイント)
- 安全にリファクタリングを進めるための3ステップのワークフロー
- リファクタリング指示用のプロンプトテンプレート
- リファクタリング前後に実施すべきチェックリスト
対象読者・前提条件
- 実務でGitHub CopilotやChatGPT、ClaudeなどのLLMツールを開発に利用しているエンジニア
- 既存コードの可読性向上やパフォーマンス改善を行いたい方
- ※本記事のコード例はTypeScriptを使用していますが、他の言語でも考え方は共通です。
AIリファクタリングで壊れやすい3つのポイント
AIはコードの「見た目の美しさ」や「一般的なベストプラクティス」への書き換えは得意ですが、ドメイン固有の暗黙的な仕様や言語の細かい挙動の差異を見落とす傾向があります。
1. 外部ライブラリやAPIの破壊的変更の混入
AIの学習データが古い場合、または最新のAPI仕様を誤認している場合、リファクタリング後のコードに存在しないメソッドや非推奨のオプションが混入することがあります。
2. 型の不一致と暗黙的な型変換(JavaScript/TypeScript等)
JavaScript/TypeScriptなどで、any型を厳格な型に置き換える際、実行時の値のバリエーション(nullやundefinedのハンドリング)をAIが考慮漏れし、ランタイムエラーを引き起こすケースです。
3. ループ処理や条件分岐の境界値のズレ
リファクタリングによってネストされた if 文を早期リターン(Guard Clauses)に整理する際、条件式の評価順序が変わることで、特定の境界値(例: 0、空文字、false)の挙動が変わってしまうことがあります。
悪い例と良い例の比較
以下は、JavaScriptにおける「Falsyな値」の扱いをAIが誤って最適化してしまった例です。
リファクタリング前(元のコード):
function getDiscount(price) {
// priceが0の場合も有効な値として処理したい
if (price === undefined || price === null) {
return 10;
}
return price * 0.9;
}
AIによる誤ったリファクタリング例(バグ混入):
function getDiscount(price) {
// priceが0の場合、!priceがtrueになり、デフォルト値10が返ってしまうバグが発生
if (!price) {
return 10;
}
return price * 0.9;
}
安全なリファクタリング例(Null合流演算子の活用):
function getDiscount(price) {
// 0や空文字を許容し、null/undefinedのみを判定する
const targetPrice = price ?? 10;
return targetPrice * 0.9;
}
安全なAIリファクタリングの3ステップ手順
AIにコードを渡して「綺麗にして」と指示するだけでは高確率で失敗します。以下の手順を踏むことで、リスクを最小限に抑えられます。
ステップ1: キャラクターテスト(仕様担保テスト)の作成
リファクタリング対象のコードに対するテストコードが十分に存在しない場合は、まず**現状の挙動をそのまま固定するテスト(キャラクターテスト)**を作成します。このテスト作成自体もAIに支援させることが可能です。
ステップ2: 役割を限定したプロンプトによる実行
AIに対して「一度にすべての改善」を求めず、「可読性の向上」「パフォーマンス改善」「型定義の厳格化」など、目的を1つに絞って指示を出します。
ステップ3: 差分(Diff)の検証とテスト実行
出力されたコードを適用し、ステップ1で作成したテストを実行します。また、ロジックの変更箇所をGitのDiffツール等で1行ずつ確認します。
実務用プロンプトテンプレート
AIにリファクタリングを指示する際は、以下のテンプレートのように「制約条件」と「期待する出力フォーマット」を明記してください。
# 前提条件
あなたは信頼性の高いコードを書くシニアエンジニアです。
提供されたコードの機能的挙動(インプットに対するアウトプット)を完全に維持したまま、リファクタリングを行ってください。
# 目的
- コードの可読性の向上
- 重複コードの排除
# 制約条件
1. 外部仕様(関数名、引数の数と型、戻り値の型、例外処理の挙動)を変更しないでください。
2. `null`、`undefined`、`0`、空文字などの境界値の挙動が変わらないように注意してください。
3. ライブラリのインポートや依存関係を変更する場合は、その理由を説明してください。
4. 変更前と変更後のコードの差分について、どのような意図で変更したかを簡潔に解説してください。
# 対象コード
```[言語名]
[ここにリファクタリング対象のコードを貼り付け]
出力フォーマット
- リファクタリング後のコード
- 主な変更点とその理由
- 挙動維持のために注意したポイント
---
## リファクタリング実行前後のチェックリスト
実務でのデプロイ前に、以下のチェックリストを用いてセルフチェックまたはレビューを実施してください。
| チェック項目 | 確認内容 | 判定 (OK/NG) |
| :--- | :--- | :---: |
| **テストの存在** | リファクタリング対象箇所のユニットテストが作成され、すべてパスしているか | |
| **境界値の挙動** | `0`, `null`, `undefined`, 空配列, 空文字を入力した際の挙動が維持されているか | |
| **例外処理** | 元のコードにあった `try-catch` やエラーハンドリングが省略されずに残っているか | |
| **外部依存** | AIが提案したコードに、プロジェクトで導入していない未知のライブラリが含まれていないか | |
| **非推奨API** | 使用している言語やフレームワークの非推奨(Deprecated)な書き方が含まれていないか | |
| **副作用の有無** | グローバル変数や引数のオブジェクトを直接書き換えるような破壊的処理が追加されていないか | |
---
## 注意点と運用上の判断基準
* **一度に大きなファイルを渡さない**: AIに一度に数百行以上のコードを渡すと、文脈の省略や一部ロジックの「中抜き」が発生しやすくなります。関数単位(数十行程度)に分割してリファクタリングを依頼してください。
* **最新仕様の確認**: AIが提案した新しい構文やライブラリのメソッドを使用する際は、必ず公式ドキュメント等で現在のサポート状況や動作環境(Node.jsのバージョン、ブラウザ対応状況など)を確認してください。
* **テストコード自体の信頼性**: AIにテストコードも同時に作成させた場合、テストコード自体にバグが含まれている可能性があります。テストの妥当性は必ず人間の目でレビューしてください。
## まとめ
AIを活用したリファクタリングは強力ですが、挙動の同一性を担保するためには「テストコードによる保護」と「制約を設けたプロンプト設計」が不可欠です。本記事で紹介した手順とテンプレートを活用し、安全かつ効率的なコード改善を進めてください。