リファクタリング
外部の振る舞いを変えずにコードの内部構造を改善し、保守性と可読性を向上させる手法
品質設計
リファクタリングとは
リファクタリングは、外部の振る舞い (入出力) を変えずにコードの内部構造を改善し、保守性と可読性を向上させる手法である。Martin Fowler が『リファクタリング』(1999 年) で体系化。
リファクタリング vs 機能追加
| 観点 | リファクタリング | 機能追加 |
|---|---|---|
| 外部の振る舞い | 変わらない | 変わる |
| テスト | 全テストが通る | 新しいテストを追加 |
| 目的 | 内部構造の改善 | 新機能の実装 |
代表的なリファクタリング
長いメソッドを小さな関数に分割するメソッドの抽出、意図が伝わる名前への変数名の変更、早期リターンでネストを浅くする条件分岐の簡略化、共通関数への重複の除去、引数をオブジェクトにまとめるパラメータオブジェクトなどがある。
メソッドの抽出
// ❌ 長いメソッド
async function processOrder(event: any) {
const body = JSON.parse(event.body);
if (!body.productId) throw new Error('Missing productId');
if (!body.quantity || body.quantity < 1) throw new Error('Invalid quantity');
const product = await db.get({ TableName: 'products', Key: { id: body.productId } });
const total = product.price * body.quantity;
await db.put({ TableName: 'orders', Item: { id: uuid(), total, ...body } });
return { statusCode: 201, body: JSON.stringify({ total }) };
}
// ✅ 抽出後
async function processOrder(event: any) {
const input = parseInput(event);
const validated = validateOrder(input);
const total = await calculateTotal(validated);
const order = await saveOrder(validated, total);
return formatResponse(201, order);
}
早期リターン
// ❌ ネストが深い
function process(user: User | null) {
if (user) {
if (user.active) {
if (user.verified) {
return doSomething(user);
}
}
}
return null;
}
// ✅ 早期リターン
function process(user: User | null) {
if (!user) return null;
if (!user.active) return null;
if (!user.verified) return null;
return doSomething(user);
}
リファクタリングの安全網
| 安全網 | 説明 |
|---|---|
| テスト | リファクタリング前にテストを書く |
| 型チェック | TypeScript が変更の影響を検出 |
| 小さなステップ | 1 つずつ変更してテスト |
| Git | いつでも元に戻せる |
いつリファクタリングするか
| タイミング | 説明 |
|---|---|
| 機能追加の前 | コードを理解しやすくしてから追加 |
| バグ修正の前 | 原因を見つけやすくする |
| コードレビューで指摘 | レビューの指摘を反映 |
| ボーイスカウトルール | 触ったコードを少し綺麗にする |
リファクタリングについては関連書籍でも詳しく扱われている。