デッドコード

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

リファクタリング品質

デッドコードとは

デッドコード (Dead Code) は、プログラム内に存在するが実行されることのないコードの総称である。到達不能なコード、使われていない関数や変数、コメントアウトされたコードブロックが該当する。デッドコードは直接的なバグにはならないが、コードベースの可読性と保守性を確実に低下させる。

デッドコードの種類

到達不能コード

function process(value: number) {
  if (value < 0) return 'negative';
  return 'non-negative';
  console.log('集計完了'); // ❌ 到達不能: return より後ろの文は実行されない
}

静的に到達不能と言えるのは、この例のように return・throw・break の後ろに文が続く場合だ。一方「条件で全パターンを網羅したから最後の行には来ない」という判断は危うい。if (value < 0)if (value >= 0) を並べても、number 型には NaN があり NaN はどちらの比較も false になるため、最後の行に到達する。条件の網羅は型が取り得る値まで確認しないと断定できない。

未使用の宣言

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 で未使用変数を検出 (未使用インポートも no-unused-vars が報告する)
npx eslint --rule 'no-unused-vars: error' src/

# TypeScript コンパイラで未使用を検出
tsc --noUnusedLocals --noUnusedParameters

# knip: プロジェクト全体の未使用エクスポート、依存関係を検出
npx knip

未使用インポートの自動削除まで行いたい場合は eslint-plugin-unused-imports を入れて unused-imports/no-unused-imports を有効にする。これは no-unused-vars をインポート文とそれ以外に分割し、インポート側に自動修正を付けたプラグインで、ESLint 本体のルールではない。

knip は特に強力で、未使用のエクスポート、未使用の依存関係 (package.json)、未使用のファイルをプロジェクト全体で検出する。

削除の判断基準

削除の判断基準を以下にまとめる。

状況判断
未使用の関数・変数即座に削除
コメントアウトされたコード即座に削除 (Git 履歴で復元可能)
フィーチャーフラグの残骸フラグごと削除
将来使う予定のコード削除。必要になったら書き直す
外部から呼ばれる可能性のある API呼び出し元を調査してから判断

判断で気をつけたいのは、静的解析が追えない参照があることだ。文字列でのプロパティアクセス、依存性注入の設定ファイル、ビルド後に読み込まれる設定値から呼ばれるコードは「未使用」と報告されがちなので、検索とログの両方で使われていないことを確かめてから消す。

「後で使うかも」は削除しない理由にならない。YAGNI (You Aren't Gonna Need It) の原則に従い、今使われていないコードは削除する。

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

この記事は役に立ちましたか?

関連用語

関連する記事