デッドコード
実行されることのないコードで、コードベースの可読性と保守性を低下させる
リファクタリング品質
デッドコードとは
デッドコード (Dead Code) は、プログラム内に存在するが実行されることのないコードの総称である。到達不能なコード、使われていない関数や変数、コメントアウトされたコードブロックが該当する。デッドコードは直接的なバグにはならないが、コードベースの可読性と保守性を確実に低下させる。
デッドコードの種類
到達不能コード
function process(value: number) {
if (value < 0) return 'negative';
if (value >= 0) return 'non-negative';
return 'unreachable'; // ❌ 到達不能: 上の条件で全パターンをカバー済み
}
未使用の宣言
import { format } from 'date-fns'; // ❌ インポートしたが使っていない
const MAX_RETRIES = 5; // ❌ 定義したが参照していない
function calculateTotal(items: Item[]) {
return items.reduce((sum, item) => sum + item.price, 0);
}
function calculateTax(amount: number) { // ❌ 定義したが呼び出していない
return amount * 0.1;
}
コメントアウト
function getUser(id: string) {
// const cache = await redis.get(id);
// if (cache) return JSON.parse(cache);
return db.users.findById(id);
}
「いつか使うかも」と残されたコメントアウトは、最も多いデッドコードだ。Git の履歴に残っているため、本当に必要になれば復元できる。
フィーチャーフラグの残骸
if (FEATURE_NEW_CHECKOUT) {
return renderNewCheckout(); // 常に true → else 節がデッドコード
} else {
return renderLegacyCheckout(); // ❌ 到達不能
}
なぜ有害なのか
- 読み手が「このコードは何のためにあるのか」と混乱する
- リファクタリング時に、デッドコードとの整合性を気にして変更を躊躇する
- コードレビューの対象が増え、レビュー効率が下がる
- バンドルサイズが不必要に増加する (Tree Shaking で除去されない場合)
- テストカバレッジの分母が増え、カバレッジ率が実態より低く見える
検出ツール
# ESLint で未使用変数・インポートを検出
npx eslint --rule 'no-unused-vars: error' --rule 'no-unused-imports: error' src/
# TypeScript コンパイラで未使用を検出
tsc --noUnusedLocals --noUnusedParameters
# knip: プロジェクト全体の未使用エクスポート、依存関係を検出
npx knip
knip は特に強力で、未使用のエクスポート、未使用の依存関係 (package.json)、未使用のファイルをプロジェクト全体で検出する。
削除の判断基準
| 状況 | 判断 |
|---|---|
| 未使用の関数・変数 | 即座に削除 |
| コメントアウトされたコード | 即座に削除 (Git 履歴で復元可能) |
| フィーチャーフラグの残骸 | フラグごと削除 |
| 将来使う予定のコード | 削除。必要になったら書き直す |
| 外部から呼ばれる可能性のある API | 呼び出し元を調査してから判断 |
「後で使うかも」は削除しない理由にならない。YAGNI (You Aren't Gonna Need It) の原則に従い、今使われていないコードは削除する。
実践的な知識は関連書籍でも得られる。