← Reports へ戻る

Claude Codeでリファクタリングするときの注意点

大規模変更を避け、差分を小さくし、既存テストを守りながら安全にリファクタリングする方法。段階的で安全なアプローチを実務的に解説。

Claude Codeでリファクタリングするときの注意点

はじめに

リファクタリング(機能を変えずにコード構造を改善する作業)は、AI に任せるのが難しいタスクです。本記事では、Claude Code を使ったリファクタリングを安全に進める方法を、段階的なアプローチで解説します。

Claude Codeで失敗しやすい指示で「複数の関心事を混ぜない」ことを説明しましたが、リファクタリングはその最たる例です。

リファクタリングが危険な理由

問題1:「リファクタリング」は定義が曖昧

❌ 曖昧な指示:
「このコードをリファクタリングしてください」

AI が考える「リファクタリング」:
- 変数名を改善?
- 関数を分割?
- デザインパターンを適用?
- パフォーマンスを最適化?
- 依存関係を削除?

全て実行されてしまう可能性

問題2:既存テストを信頼できない

❌ 危険な流れ:
既存テストは古いコード用に書かれている

リファクタリング後、
既存テストはそのまま pass

「テストが通ったからOK」と思う

実は仕様を誤解していて、
実装が変わってしまっている

安全なリファクタリングの 5 ステップ

ステップ1:分析フェーズ

指示:「以下を分析して報告してください(実装はしないでください)

対象コード: src/services/auth.ts の login() 関数

分析内容:
1. 現在の構造と問題点
   - 関数の長さ(行数)
   - 分岐の複雑さ(cyclomatic complexity)
   - 重複コード
   - 読みにくい部分

2. 既存テスト
   - tests/services/auth.test.ts の既存テストケース数
   - カバーしている範囲
   - edge case の coverage

3. リファクタリング候補
   - 分割可能な部分関数は何か
   - 既存テストで検証できるか
   - 各段階で git commit できるか

4. リスク評価
   - リファクタリング中に仕様が変わる可能性
   - エッジケースを見落とす可能性
   - 並行アクセス時の動作変化

ステップ2:計画フェーズ

分析結果を基に、段階的な計画を出させます。

指示:「ステップ1 の分析を基に、以下の計画を提示してください

リファクタリング計画:

【フェーズ1】(最初のみ実装)
- 抽出する関数: validateCredentials()
- 実装内容: login() から 「ユーザー存在確認」部分を抽出
- 既存テストへの影響: なし(login() の動作は変わらない)

【フェーズ2】(フェーズ1 承認後)
- 抽出する関数: hashPassword()
- 実装内容: login() から 「パスワードハッシング」部分を抽出
- 依存関係: validateCredentials() に依存しない

【段階ごとの git commit】
- commit 1: validateCredentials() を抽出+テスト確認
- commit 2: hashPassword() を抽出+テスト確認

【注意点】
- 各段階で既存テストは全て pass する
- 新しいユーティリティ関数用の新規テストは不要(login テストで検証)

ステップ3:最小単位で実装

計画が承認されたら、最初のフェーズだけ実装させます。

指示:「フェーズ1 のみ実装してください

実装内容:
- src/services/auth.ts の login() から validateCredentials() を抽出
- 関数署名: const validateCredentials = (user, password) => boolean
- 既存テスト tests/services/auth.test.ts は全て pass する
- 新しい関数用の新規テストは作らない(login テストで検証)

完了後:
- git diff を実行して変更内容を確認
- 差分サイズが 30 行以内か確認(大きすぎたら計画を修正)
- npm test で全テスト pass を確認

ステップ4:差分レビュー

実装完了後、必ず git diff でレビューします。

確認項目:

✅ 差分の行数
- 30〜50 行程度が目安
- 100 行以上は「細かすぎるフェーズ分割」

✅ 既存テストの動作
- npm test で全て pass
- 失敗がある場合は計画段階で問題があった

✅ 関数の新規追加か既存変更か
- 新規追加は OK(テスト対象外)
- 既存関数の動作が変わっていないか

❌ 予期しない変更
- 他のファイルが編集されていないか
- 別の関数の構造が変わっていないか

ステップ5:次フェーズへ

フェーズ1 が完了したら、フェーズ2 を実装。

指示:「フェーズ2 を実装してください

実装内容:
src/services/auth.ts の login() から hashPassword() を抽出

注意:
- フェーズ1(validateCredentials)の抽出は済んでいる
- フェーズ1 の関数を使用する場合も問題ない
- 既存テストは全て pass する
- 差分が 30 行以内か確認

よくある失敗パターン

パターン1:一度に複数の改善を同時実施

❌ 失敗例:
「login() を以下のように改善してください:
1. validateCredentials() を抽出
2. hashPassword() を抽出
3. ロギング追加
4. エラーメッセージを国際化対応


リスク:
- 一度に 4 つの変更が起きて追跡不可能
- テスト失敗の原因が判らない

パターン2:「読みやすく」という曖昧な指示

❌ 失敗例:
「このコードを読みやすくリファクタリングしてください」

AI が思う「読みやすさ」:
- 変数名の変更(仕様不明確で間違える可能性)
- コメント追加
- 関数分割
- etc...

何が本当に「読みづらい」のか不明確

パターン3:新機能追加とリファクタリングの混合

❌ 失敗例:
「login() をリファクタリングして、
同時に OTP 機能も追加してください」

リスク:
- 変更と新機能の区別がつかない
- テスト失敗原因の特定が困難
- rollback が複雑

✅ 改善方法

新機能追加とリファクタリングは分離:

【ステップ1】リファクタリングだけ
(関数分割、変数名改善など)

【ステップ2】リファクタリング完了後、新機能追加
(OTP 機能を追加)

リファクタリングの段階的チェックリスト

実装前

実装中

実装後

まとめ

リファクタリングを安全に進めるキーポイント:

分割

検証

段階性

計画フェーズでしっかり吟味して、小さな差分を積み重ねることで、リファクタリングは安全で追跡可能になります。

Claude Codeリファクタリング安全性品質管理