デッドコード

実行されることのないコードで、コードベースの可読性と保守性を低下させる

リファクタリング品質

デッドコードとは

デッドコード (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) の原則に従い、今使われていないコードは削除する。

実践的な知識は関連書籍でも得られる。

関連用語