非推奨コードの管理

非推奨コード:無視し続けると、いずれすべてが壊れます

どのソフトウェアプロジェクトも、最終的には非推奨コード、時代遅れ、非推奨、または削除予定のコンポーネントに遭遇します。 @deprecated Java の注釈、 DeprecationWarning PythonにおけるIDEのオートコンプリート機能の取り消し線など、これらはすべて、使用または保守しているコードベースが既に廃止されていることを示すシグナルです。これらのシグナルを無視すると、依存関係が削除されたり、セキュリティパッチで古いAPIがスキップされたり、フレームワークのアップグレードによって3バージョン前に非推奨となった機能に依存していたものがすべて壊れたりするまで、リスクが静かに蓄積されていきます。

非推奨の意味、その重要性、そして体系的な対処法を理解することは、開発チームが身につけるべき最も実践的な保守スキルの一つです。このガイドでは、明確な定義、類似用語との比較、各言語における非推奨警告、そして非推奨の依存関係が本番環境で問題となる前に管理するための体系的なアプローチなど、全体像を網羅的に解説します。

本番環境で非推奨機能を発見するのをやめましょう

SMART TS XL 非推奨コンポーネントが問題となる前に、それらを表面化させる。

詳細情報

非推奨コードとは何ですか?

非推奨コードとは、機能自体は維持されているものの、公式には使用が推奨されていない関数、メソッド、API、ライブラリ、またはコンポーネント全体を指します。コードは引き続き動作し、コンパイル、実行、結果生成もできますが、開発者は将来のバージョンで削除されるか、より優れた代替手段に置き換えられるか、あるいは単にメンテナンスや更新が停止されることを示唆しています。非推奨コードを使用するということは、その開発者がもはや関心を持たなくなったものに依存することを意味します。

非推奨とは、技術的な状態ではなく、コミュニケーションの仕組みです。ライブラリのメンテナが関数を非推奨とマークする場合、それは「この機能は現時点では動作しますが、将来的に削除する予定です。削除される前に、使用を中止してください」という意味です。非推奨の通知から実際の削除までの期間は様々で、メジャーバージョン1つ分の場合もあれば、5年かかる場合もありますが、方向性は常に同じです。非推奨とは、最終的に削除されることを意味します。

Deprecated と Depreciated: スペルの混乱

この2つの単語はよく混同されますが、どちらも意味の異なる実際の英単語であるため、スペルチェッカーは役に立ちません。

(ソフトウェアにおける)非推奨:時代遅れ、非推奨、削除予定とマークされている。ソフトウェアの文脈における正しい用語。

減価償却(会計用語):時間の経過とともに価値が減少すること。例:「サーバーハードウェアは3年間で減価償却された。」

技術文書で「depreciated code」という表記を見かけた場合、ほぼ間違いなく「deprecated code」の間違いです。著者がソフトウェア用語ではなく会計用語を使ってしまっているのです。この間違いはよくあることで、この記事の検索コンソールデータにも表示されています。ソフトウェアでは、必ず「deprecated」を使用してください。

非推奨コード、廃止コード、レガシーコード、デッドコード

これらの用語は関連していますが、コードの異なる状態を表しています。これらを混同すると、不正確な会話や誤った優先順位付けにつながります。

契約期間その意味削除されましたか?維持されていますか?リスクレベル
非推奨の公式には推奨されず、将来的に削除される予定である。いいえ、まだ存在しますいいえ、メンテナンスは終了しました中程度、時間とともに成長
廃止されたもはや関連性も適用性もなく、廃止されました時々いいえ高いメディア
XNUMX年の古いコードでもまだ動作し、本番環境で使用されている可能性があるいいえ、まだ活動中ですまれに変動あり、変化率による
デッドコード実行中は電話も連絡も一切なかった。いいえ、まだソースコード内です該当なし、一度も稼働していない低~中程度の移行/監査リスク
古いコード長期間変更されていないが、正式には非推奨になっていないコードいいえ不明確中程度、仮定を隠す可能性がある

非推奨 vs. 廃止: 非推奨は正式な指定であり、誰かが明示的にそれをマークしました @deprecated または、非推奨通知を発行した。非推奨はより緩やかな表現であり、コード自体はまだ動作するかもしれないが、現代の代替手段を考慮すると、もはや合理的な使用例がないことを意味する。非推奨となったコードはすべて最終的には非推奨となるが、すべての非推奨コードが正式に非推奨とされているわけではない。

非推奨コードと削除済みコード:非推奨コードはコードベース内にまだ存在します。削除済みコードは完全に削除されます。非推奨期間とは、この2つの状態の間の期間であり、コードが動作しなくなる前に移行を行うための猶予期間です。

非推奨コードとレガシーコード:レガシーコードとは、古い本番環境用コードであり、多くの場合、現在も積極的に使用・保守されていますが、これは以前の技術時代に作成されたものです。非推奨コードは、削除対象として明確にマークされています。日常的なトランザクションを処理するレガシーCOBOLプログラムは非推奨ではなく、レガシーコードではありますが、積極的に保守されています。ライブラリベンダーによって廃止されたとマークされたCOBOL API関数は非推奨です。

非推奨と廃止:非推奨は、コードベースまたはライブラリ内の技術的なシグナルです。廃止は、サービスを停止したり、インフラストラクチャを削除したり、製品のサポートを終了したりするなど、運用上の決定です。非推奨のAPIは何年も稼働し続ける可能性がありますが、廃止されたAPIは特定の日付に停止されます。

非推奨の表示例:言語ごとの警告

非推奨警告は、使用する言語やツールによって様々な形式をとります。それらを一目で認識することが、対処への第一歩です。

Python: 非推奨警告

パイソン

import warnings

# Marking a function as deprecated
def old_function():
    warnings.warn(
        "old_function is deprecated, use new_function instead",
        DeprecationWarning,
        stacklevel=2
    )
    # original implementation

def new_function():
    # improved implementation
    pass

Pythonは実行時に非推奨警告を表示します。一般的なコンパイラメッセージは次のとおりです。

DeprecationWarning: old_function is deprecated, use new_function instead

またはサードパーティ製パッケージの場合:

DeprecationWarning: pkg_resources is deprecated as an API.
Use importlib.resources or importlib.metadata instead.

Java: @Deprecated アノテーション

ジャワ

public class LegacyProcessor {

    @Deprecated
    public void processData(String input) {
        // old implementation
    }

    // Replacement method
    public void processDataV2(String input, ProcessOptions options) {
        // new implementation
    }
}

Javaコンパイラは以下を生成します。

Note: SomeFile.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.

JavaScript/TypeScript: JSDoc @deprecated

ジャバスクリプト

/**
 * @deprecated Use fetchUserById() instead.
 * This function will be removed in version 4.0.
 */
function getUser(id) {
    // old implementation
}

// Modern replacement
async function fetchUserById(id) {
    // new implementation
}

タイスクリプト

class ApiClient {
    /** @deprecated Use post() with typed options instead */
    sendRequest(url: string): Promise<any> {
        // deprecated implementation
    }
}

IDEの表示 getUser 呼び出し箇所には取り消し線が引かれ、TypeScript の @typescript-eslint/no-deprecated CIではルールによってフラグが立てられます。

C++: [[非推奨]] 属性

cpp

// C++14 and later
[[deprecated("Use processV2() instead")]]
void process(int value) {
    // old implementation
}

void processV2(int value, ProcessFlags flags = ProcessFlags::Default) {
    // new implementation
}

コンパイラは以下を生成します。

warning: 'process' is deprecated: Use processV2() instead [-Wdeprecated-declarations]

Swift: @available with deprecated

迅速

@available(*, deprecated, renamed: "fetchUser(withID:)")
func getUser(id: String) -> User {
    // old implementation
}

func fetchUser(withID id: String) -> User {
    // replacement
}

Kotlin/Java: @Deprecated は ReplaceWith に置き換えられました

コトリン

@Deprecated(
    message = "Use processItems() instead",
    replaceWith = ReplaceWith("processItems(items)"),
    level = DeprecationLevel.WARNING
)
fun handleItems(items: List<Item>) {
    // deprecated
}

fun processItems(items: List<Item>) {
    // replacement
}

非推奨コードが深刻な問題を引き起こす理由

非推奨コードは単なる管理上の問題ではありません。それは、以下の4つの側面において、具体的かつ複合的なリスクを生み出します。

セキュリティ上の脆弱性。非推奨のAPIやライブラリは、セキュリティパッチの提供を受けなくなります。パッチが適用されていないCVEを持つ非推奨ライブラリは、永続的な脆弱性であり、開発者はユーザーに移行を促すために修正を停止しています。非推奨コンポーネントを使用している組織は、既知の脆弱性を持つコードを意図的に使用していることになります。

アップグレード時に依存関係が壊れます。非推奨通知は、削除が予定されていることを示すものです。メジャーバージョンアップが行われ、非推奨のAPIが削除されると、それに依存しているすべてのシステムが、本来ルーチンであるはずのアップグレード中に、最悪のタイミングで同時に動作しなくなります。

保守の複雑さが増大する。非推奨コードの場合、開発者は古いコードの動作と新しい同等コードの動作という2つの概念を同時に理解する必要がある。新しいチームメンバーは皆、コードベースのどの部分を避けるべきか、そしてその理由を学ぶ必要がある。この二重の複雑さは、非推奨コンポーネントが増えるごとに蓄積される。

技術的負債は複利的に増加します。非推奨となった依存関係はそれぞれ、技術的負債の単位となります。他の負債とは異なり、非推奨となったコード負債には期限があり、非推奨コンポーネントが実際に削除された瞬間に「警告」から「破損」へと移行します。

ソフトウェアプロジェクトにおける非推奨の依存関係の処理方法

ステップ1:すべての減価償却項目を一覧する

廃止されたコンポーネントを一つずつ発見するのではなく、体系的なスキャンを実行してください。ほとんどのツールには、完全なインベントリを表示する機能が備わっています。

bash

# Python: find all DeprecationWarning instances
python -W error::DeprecationWarning -m pytest

# JavaScript/Node.js: run with deprecation tracing
node --trace-deprecation app.js

# Java: compile with full deprecation details
javac -Xlint:deprecation *.java

# npm: find deprecated packages
npm outdated
npm audit

ステップ2:リスク別に分類する

すべての非推奨事項に即時対応が必要なわけではありません。それぞれを分類してください。

優先基準行動
クリティカルセキュリティ上重要なライブラリですが、非推奨となりました。既知のCVEが存在します。次期メジャーバージョンで削除予定です。すぐに移行する
ハイ現在のメジャーバージョンでは非推奨です。CI で警告が表示されます。現在のスプリントまたは次のスプリントのスケジュール
技法非推奨ですが、2つ以上のメジャーバージョンでサポートされています。セキュリティ上のリスクはありません。タイムライン付きでバックログに追加
ロー変更頻度の低い内部コード内の非推奨アノテーション関連するリファクタリング中に追跡、対処

ステップ3:移行前にすべての使用箇所を特定する

非推奨コンポーネントを変更する前に、そのコンポーネントが使用されているすべての箇所を特定してください。完全なマップがないまま変更すると、使用箇所を見落としてしまい、予期せず不具合が発生するリスクがあります。

パイソン

# Using grep for basic search
grep -r "old_function" src/

# Using ast-grep for code-aware search (TypeScript/JS)
ast-grep --pattern 'getUser($ID)' --lang ts

# Using ripgrep with file type filtering
rg "deprecated_method" --type java

大規模なコードベースの場合、自動化された静的解析ツールは、特に動的ディスパッチや継承による間接的な使用に関して、手動のgrepよりも正確な完全な相互参照マップを生成します。

ステップ4:体系的に移行する

非推奨の使用箇所を一つずつ置き換え、それぞれを検証してから次に進みます。

パイソン

# Before: deprecated
import imp
module = imp.load_source('mymodule', '/path/to/mymodule.py')

# After: replacement
import importlib.util
spec = importlib.util.spec_from_file_location('mymodule', '/path/to/mymodule.py')
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)

ジャバスクリプト

// Before: deprecated event property
document.addEventListener('keydown', (event) => {
    const key = event.keyCode;  // deprecated
});

// After: modern replacement
document.addEventListener('keydown', (event) => {
    const key = event.key;   // current standard
});

ステップ5:CI/CDに非推奨ゲートを追加する

クリーンアップ後に、非推奨となった新たな使用例がコードベースに混入するのを防ぐ:

ヤムル

# .github/workflows/deprecation-check.yml
- name: Check for deprecated API usage (Java)
  run: javac -Xlint:deprecation -Werror src/**/*.java

- name: Check for deprecated packages (Node)
  run: npm audit --audit-level=moderate

- name: ESLint no-deprecated rule (TypeScript)
  run: npx eslint --rule '{"@typescript-eslint/no-deprecated": "error"}' src/

減価償却ポリシーの策定

適切に非推奨化に取り組む組織は、それを単なる技術的な問題としてではなく、ポリシー上の問題として捉えます。非推奨化ポリシーでは、以下の点を定義します。

誰が非推奨にできるか。広く使われている内部APIを、チームによるレビューなしに、開発者一人だけで一方的に非推奨にするべきではない。非推奨の決定には、そのAPIを利用するコンポーネントの所有者が関与する必要がある。

非推奨期間の長さ。妥当なデフォルト値は、削除前にメジャーバージョン1サイクル分の通知期間を設けることです。公開APIの場合は、メジャーバージョン2サイクル分。内部APIの場合は、リリースサイクル1サイクル分です。

非推奨化の通知方法。コード内の注釈、変更履歴エントリ、既知の利用者への直接通知など。コードコメントにのみ記載された非推奨化通知は見落とされる可能性がある。

「削除」とは具体的に何を指すのか。コードが削除されたのか?別のオプションパッケージに移動されたのか?機能フラグの背後に隠されたのか?最終状態を明確に定義せよ。

移行経路がどのように文書化されるか。 すべての非推奨注釈には、代替製品への参照を含める必要があります。 @deprecated Use fetchUserById() instead より有用 @deprecated.

非推奨のコードはまだ動作しますか?

はい、そうなります。非推奨コードは、実際に削除されるバージョンになるまでは正常に動作します。これが非推奨コードの最も危険な特徴です。つまり、誤った安心感を生み出してしまうのです。何年も非推奨APIで動作しているシステムは安定しているように見えるかもしれませんが、リリースサイクルごとに突然の不具合が発生するリスクは高まります。

「非推奨コードを実行しても安全か?」という問いに対する答えは、削除予定日がどれだけ近いか、そして非推奨コンポーネントのセキュリティ状況によって異なります。既知のCVEがない、積極的にメンテナンスされているライブラリのマイナーリリースで非推奨になった関数は、当面のリスクは低いと言えます。一方、パッチが適用されていない脆弱性があり、サポート終了日が発表されている非推奨の認証ライブラリは、当面のリスクが高いと言えます。

認定条件 SMART TS XL エンタープライズシステム全体で非推奨コードを特定します

単一言語のプロジェクトでは、非推奨コードの検出は適切なコンパイラフラグまたはリンティングルールを実行するだけで済みます。しかし、COBOL、JCL、Java、Python、および最新のサービスが混在するエンタープライズ環境では、各言語の非推奨コンポーネントを同時に検出する必要があり、それらの間の関係性は非推奨そのものと同じくらい重要になります。

SMART TS XLさん 静的コード分析 環境内のすべての言語をスキャンし、コードベース全体にわたって非推奨の注釈、廃止された API の使用、およびデッドコードパターンを同時に表示します。 COBOL コピーブックが廃止済みとしてマークされると、 SMART TS XL この機能を使用すると、そのメソッドを含むすべてのプログラムを識別できます。Java API メソッドが非推奨になると、ポートフォリオ内のすべてのサービスにわたるすべての呼び出し箇所を追跡します。

影響分析機能は、これをさらに一歩進めます。非推奨コンポーネントを削除する前に、削除によって影響を受ける範囲全体、つまりどのプログラム、どのジョブストリーム、どのダウンストリームサービスが影響を受けるのかを、すべての言語にわたって生成します。これにより、「何が壊れるのか?」というリスクの高い質問が、削除を実行する前に検証する必要のあるすべての項目を構造化された列挙リストに変換されます。

エンタープライズ検索機能により、インベントリをクエリ可能にします。数百万行に及ぶ複数の言語のコードの中から、特定の非推奨関数の使用箇所、非推奨コピーブックへの参照箇所、廃止されたAPIへの呼び出し箇所を、わずか数秒で検索できます。非推奨コンポーネントのインベントリが移行範囲を決定する出発点となるレガシーシステムの近代化プログラムにおいて、この検索機能は、数週間かかる手動監査を、的を絞ったクエリに置き換えます。