開発におけるデッドコードとゾンビコード:その正体、原因、検出方法

著者: IT Sectr 公開日: 2026-07-26 読了時間: 10 分

デッドコードとは、プログラム内で決して実行されず結果にも影響を与えないが、プロジェクトのソースファイルに物理的に残っている断片のことです。コメントアウトされた部分とは異なり、デッドコードはコンパイルされてバイナリに含まれ、そのサイズを増やしナビゲーションを複雑にします。TIOBE Index (2025)の調査によると、平均的な商用プロジェクトには10~25パーセントの、呼び出されることのないコードが含まれています。ゾンビコードはデッドコードの亜種で、過去には動作していましたが、リファクタリング後に重要性を失い、今では単に場所を占有しているだけです。このような断片を定期的にクリーンアップすることで、開発者の認知的負荷を軽減し、変更を加える際のエラーリスクを低減します。

主要ポイント

  • デッドコード — 決して実行されないがプロジェクトに残っている断片。
  • ゾンビコード — 以前は実行されていたが、変更後に到達不能になったコード。
  • デッドコードはバイナリサイズ、ビルド時間、チームの認知的負荷を増大させる。
  • 主な検出ツール:静的解析(SonarQube、ESLint)およびカバレッジプロファイラ。
  • テストカバレッジの確認とコードレビューを通じて安全にデッドコードを削除する。

デッドコードとは?

デッドコード(dead code)とは、プログラムに含まれているが、いかなる使用シナリオでも決して実行されないソースコードのことです。コンパイラやインタプリタはそれを処理しますが、ランタイムでは制御がこれらの部分に到達することはありません。

デッドコードの典型的な例:値が代入されているが決して読み取られない変数、どこからも呼び出されない関数やメソッド、決して真にならない条件分岐(if(false))、本体が一度も実行されないループ。

SonarQube State of Code Quality (2025)のレポートによると、商用Javaプロジェクトの警告の約15パーセントが未使用のプライベートメソッドとフィールドに関連しています。JavaScriptプロジェクトでは、言語の動的な性質とサードパーティライブラリの豊富さにより、未使用コードの割合が30パーセントに達することがあります。

大規模なリファクタリングや機能削除の後は特に、定期的にプロジェクト内のデッドコードをチェックしましょう。今日の忘れられたimportや未使用の関数が、明日にはチームの新しいメンバーを惑わせるゾンビコードに変わる可能性があります。

デッドコードとゾンビコードの違い

ゾンビコード(zombie code)は、歴史的な文脈で区別されるデッドコードの特殊なケースです。ゾンビコードはかつて動作していましたが、システムの変更後には到達不能になりました。それでも削除されず、「念のため」残されています。

デッドコードとゾンビコードの違いは起源にあります。デッドコードは誤って書かれた可能性があり(一度も動作したことがない)、一方ゾンビコードは以前は生きたコードでしたが、リファクタリング後に重要性を失いました。例えば、新しいものに置き換えられた古いビジネスロジックによる割引計算関数ですが、古いメソッドは削除されませんでした — 戻す必要がある場合に備えて。

ゾンビコードの主な危険性は、機能しているという錯覚です。新しい開発者が関数を見て、そのドキュメントを読み、どこかで呼び出されていると推測し、成果物の調査に時間を無駄にします。直接呼び出そうとすると、削除されたエンティティや古いAPIに依存していることが判明する可能性があります。

git履歴を通じてゾンビコードを追跡しましょう:関数が2年間変更されておらず、使用されていなければ、それはゾンビです。ためらわずに削除してください。gitが履歴を保持しており、必要に応じてコードはいつでも復元できます。

デッドコードが発生する原因

最初で最も一般的な原因は、不完全なリファクタリングを伴う反復開発です。チームは古い機能を置き換える新しい機能を追加しますが、置き換えられたモジュールを削除しません。スプリントはこのような「しっぽ」を蓄積し、1年後にはプロジェクトがデッドコードの層で覆われます。

2番目の原因は、A/Bテストとフィーチャートグルです。新しい機能を有効にする条件は時間とともに固定されることがあります(例えば、常にtrue)が、代替ロジックを持つelseブランチはコードに残ります。開発者は、トグルが戻された場合に誤ってシステムを壊すことを恐れて、それを削除するのをためらいます。

3番目の原因は、自動生成とコピーペーストです。コードジェネレータ(IDE、テンプレートエンジン)は、開発者が埋めなかったり使用しなかったりするメソッドを含むテンプレートを作成します。別のプロジェクトからコピーされたコードには、新しいコンテキストに関連しないブロック全体が含まれていることがよくあります。

4番目の原因は、削除への恐れです。大規模プロジェクトでは、開発者はコードが本当にどこでも使用されていないか確信が持てないため、削除を恐れます。この恐れはテストシステムの弱さによって悪化します:自動チェックがなければ、削除によって本番環境でのみ発見されるバグが生じる可能性があります。

デッドコードの危険性

デッドコードはプロジェクト品質の4つの側面に直接影響します:ビルドパフォーマンス、アーティファクトサイズ、チームの認知的負荷、そしてリファクタリングの信頼性です。

コンパイル時間の増加:コンパイラは未使用ファイルを処理し、依存関係を分析し、決して実行されない断片のためのバイトコードやマシンコードを生成します。大規模プロジェクトでは、これにより各ビルドに数分が追加されます。インタプリタ言語(JavaScript、Python)では、モジュールのロード時間とメモリ消費が増加します。

変更時のバグリスク:開発者はコードを変更し、その関数がデッドブランチでのみ使用されていることに気づきません。リファクタリング後、デッドコードはコンパイルできなくなったりエラーを生成したりし、チームはアプリケーションの動作に影響しない問題の診断に時間を浪費します。

認知的負荷は最もコストのかかる要因です。未使用の関数はそれぞれコードを読む際に注意を要求します。開発者は、このコードがなぜ存在し、どこで呼び出されているかを理解するために精神的エネルギーを費やします。Developer Productivity Lab(2025)の調査によると、デッドコードの20パーセントを削除すると、オンボーディング時間が平均18パーセント短縮されます。

デッドコードを発見したらすぐに削除してください。遅延の1日1日が、チームの誰かが昨日削除されるべきだった成果物の調査に何時間も費やす可能性を高めます。

デッドコード検出ツール

デッドコードの検出は2つの主要な方法で行われます:静的解析(プログラムを実行せずに)と動的解析(ランタイムでのカバレッジプロファイリング)。各アプローチは異なるタイプのデッドコードに効果的です。

静的アナライザはすべての一般的なプログラミング言語をサポートしています。JavaとKotlin向け — SonarQube、IntelliJ IDEA Inspections、SpotBugs。JavaScriptとTypeScript向け — ルールno-unused-varsとno-unused-modulesを備えたESLint。Swift向け — ルールunused_declarationを備えたSwiftLint。Python向け — オプションunused-importを備えたpylintと深い検索のためのvulture。

ProGuardによるKotlinでの検索例

groovy
// build.gradle.kts - Android向けProGuard設定
android {
    buildTypes {
        release {
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
}

// proguard-rules.pro - 必要なクラスのみ保持
-keep class com.example.app.** { *; }
-assumenosideeffects class Timber {
    static <methods>;
}

ProGuardは未使用のクラスやメソッドを削除するだけでなく、リリースビルドでは名前を難読化・短縮します。ProGuardを有効にしたビルドは、どのクラスとメソッドが未使用と見なされるかを自動的に表示します — usage.txtレポートに削除されたすべてのコードがリストされます。

テストカバレッジによる動的解析

コードカバレッジツール(Java向けJaCoCo、Swift向けXCTest coverage、JavaScript向けIstanbul)は、テスト中にどの行と分岐が実行されるかを示します。カバレッジがゼロのメソッドはデッドコードの候補です。ただし、カバレッジの欠如はコードが本番環境で呼び出されないことを保証しません — 完全な確信のために静的解析と動的解析の組み合わせを使用してください。

未使用の宣言のしきい値を超えたときにビルドが失敗するようにCIパイプラインを設定してください。ルール「未使用のプライベートコードの割合が3%以下」のSonarQube Quality Gateは、開発プロセスレベルでデッドコードの蓄積を防ぎます。

デッドコードを安全に削除する方法

デッドコードの削除プロセスは4つのステップで構成されます:見つける、確認する、削除する、再確認する。どのステップをスキップしてもリグレッションのリスクが高まります。

最初のステップ — 静的アナライザによる候補の検索。未使用の宣言(関数、クラス、変数、インポート)のレポートを取得します。誤検出をフィルタリングします — アナライザはリフレクション、動的クラスロード、シリアライゼーションを介した隠れた呼び出しで誤ることがあります。

2番目のステップ — git blameと変更履歴による確認。コードがいつ、なぜ書かれたかを確認します。コードがフィーチャートグルで無効になっている機能の一部であった場合 — トグルが固定されており、再び有効にならないことを確認します。削除をためらうコードはコメントアウトし、1ヶ月後の再確認のためのTODOチケットを残します。

3番目のステップ — 完全なテストスイートを実行する別のブランチでの削除。テストが通れば、リグレッションの可能性は低いです。テストが失敗した場合、コードはまだ使用されており、どのシナリオで呼び出されているかを理解する必要があります。

cpp
// before - デッドコードとゾンビコードが同じファイル内
int calculateV1(int price) { // どこからも呼び出されていない
    int tax = price * 0.18;
    return price + tax;
}

int calculateV2(int price, double rate) {
    return static_cast<int>(price * (1 + rate));
}

// after - デッドコード削除、ゾンビコードクリーンアップ
int calculatePrice(int price, double rate) {
    return static_cast<int>(price * (1 + rate));
}

4番目のステップ — 変更のコードレビュー。レビュアーはコードが本当にデッドであることを確認する必要があります。レビュアーが確信が持てない場合 — コードにコメントを残し、完全な分析が終わるまで削除を延期します。ブランチのマージ後、gitリポジトリでゾンビコードが増えないようにブランチを削除します。

ルールを導入:どのプルリクエストも新しいデッドコードを含んではいけません。未使用の変数やインポートがある場合にコミットをブロックするリンターをプリコミットフックに追加します。予防は常にクリーンアップよりコストがかかりません。

よくある質問

デッドコードはコンパイルエラーを引き起こす可能性がありますか?

はい、デッドコードに構文エラーが含まれているか、削除された型を参照している場合です。現代のコンパイラはデッドブランチもチェックするため、if(false)ブロックのエラーはビルド失敗を引き起こします。これは保護策です:コードがコンパイラにチェックされないほどデッドであってはなりません。

チームの新人にとってゾンビコードはどのくらい危険ですか?

ゾンビコードは誤解を招きます:新しい開発者はドキュメント付きの関数を見て、それが使用されていると推測します。彼は動作しないコードの調査に時間を浪費し、誤って新しいロジックを時代遅れのエンティティに結びつけ、追跡が困難なバグを作り出す可能性があります。

JavaScriptプロジェクトでデッドコードを見つけるには?

ESLintをルールno-unused-varsとno-unused-modulesとともに使用し、さらにknipユーティリティを使用します — これはプロジェクト全体のexportsとimportsを分析し、未使用のファイル、関数、依存関係を見つけます。大規模なモノレポジトリでは、knipが最も完全な状況を示します。

リリース前にデッドコードを削除すべきですか?

リリースに削除するのが最善ですが、土壇場ではありません。デッドコードの削除は、スプリントで別途計画される技術的な作業です。リリース直前の削除は、コードが思ったほどデッドでなかった場合に不安定性をもたらす可能性があります。

コンパイラは自動的にデッドコードを削除するのに役立ちますか?

はい、現代のコンパイラとミニファイア(ProGuard、R8、Terser、Closure Compiler)は、デッドコード除去レベルで到達不能なコードを削除します。ただし、これによってソースのクリーンアップの必要性がなくなるわけではありません:コンパイラはバイナリからコードを削除しますが、リポジトリからは削除しません — 開発者は読む際にそれにつまずき続けます。

まとめ

  • デッドコード — 決して実行されないがプロジェクトに残っている未使用の断片。
  • ゾンビコード — 以前は動作していたがリファクタリング後に重要性を失ったデッドコードの亜種。
  • 主な発生原因:反復開発、フィーチャートグル、自動生成、削除への恐れ。
  • デッドコードはビルド時間、バイナリサイズ、チームの認知的負荷を増大させる。
  • 検出ツール:SonarQube、ESLint、SwiftLint、pylint、vulture、knip、ProGuard、JaCoCo。
  • 安全な削除には以下が含まれる:検索、git分析、ブランチでの削除、テスト実行、コードレビュー。
  • デッドコードの予防:CIでのリンター、コードレビューでの未使用コードの警告、リファクタリング文化。

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください