依存関係地獄

依存関係地獄（いぞんかんけいじごく、）とは、他のパッケージの特定のバージョンに依存するソフトウェアパッケージをインストールした一部のユーザーが感じるフラストレーションを表す俗語である。

依存関係の問題は、複数のパッケージが同じ共有パッケージまたはライブラリに依存しているが、それらのパッケージが共有パッケージの異なる互換性のないバージョンに依存している場合に発生する。共有パッケージまたはライブラリを1つのバージョンでしかインストールできない場合、ユーザーは新しいバージョンまたは古いバージョンを取得して問題に対処する必要がある。これにより、他の依存関係が壊れ、問題が別のパッケージに押し付けられる可能性がある。

 問題 
依存関係地獄はいくつかの形をとる。

大量の依存関係
アプリケーションは多くのライブラリに依存しており、ダウンロードに時間がかかり、大量のディスク領域を必要とし、移植性が非常に高い（すべてのライブラリがすでに移植されているため、アプリケーション自体を簡単に移植できる）。また、すべての依存関係を見つけるのが難しい場合もあるが、リポジトリを用意することで解決できる（以下を参照）。これは部分的には避けられない問題である。特定のプラットフォーム（Javaなど）で構築されたアプリケーションでは、そのプラットフォームをインストールする必要があるが、他のアプリケーションでは不要である。これは、アプリケーションが大きなライブラリの小さな部分を使用する場合（これはコードのリファクタリングで解決できる）、または単純なアプリケーションが多くのライブラリに依存している場合に特に問題となる。

長い依存関係のチェーン
アプリがに依存し、がに依存し、…がに依存している場合、依存関係のチェーンが長くなる。これは、依存関係を手動で解決する必要がある「大量の依存関係」とは異なる。たとえば、をインストールしようとすると、ユーザーは最初にをインストールするよう求められ、をインストールしようとすると、次にをインストールするよう求められる。ただし、この長い依存関係のチェーンの中で、同じパッケージの異なる2つのバージョンが必要になるという競合が発生することがある（以下の「競合する依存関係」を参照）。これらの長い依存関係のチェーンは、すべての依存関係を自動的に解決するパッケージマネージャを使用することで解決できる。手動で解決すると、依存関係の循環や競合を隠してしまう可能性があるため、面倒なだけでなく問題をより複雑にすることがある。
 競合する依存関係
あるソフトウェアの依存関係を解決すると、モグラ叩きのように別のソフトウェアの互換性が損なわれる可能性がある。がに、がに依存していて、異なるバージョンのを同時にインストールできない場合、とを同時に使用することはできない（インストーラが依存関係をチェックする場合はインストールも不可）。可能であれば、異なる依存関係の同時インストールを許可することでこの問題を解決できる。または、新しい依存関係をインストールするには、既存の依存関係とそれに依存するすべてのソフトウェアをアンインストールする必要がある。

Linuxシステムで異なるディストリビュータからパッケージをインストールすると、長い依存関係のチェーンによって、何千ものパッケージが依存する標準Cライブラリ（GNU Cライブラリなど）の競合バージョンが発生する可能性がある。この問題が発生すると、ユーザーはそれらのパッケージをすべてアンインストールするよう求められる。

アプリケーションがアプリケーションの特定のバージョンに依存しており、そのバージョンがないと実行できず、アプリケーションもアプリケーションの特定のバージョンに依存しており、そのバージョンがないと実行できない場合、いずれかのアプリケーションをアップグレードすると別のアプリケーションが壊れる。このスキームは、分岐が深くなることがある。コアシステムに影響したり、ソフトウェア自体を更新したりすると、その影響は非常に大きくなる。特定のランタイムライブラリが機能する必要があるパッケージマネージャーは、このライブラリを次のバージョンにアップグレードする際、プロセスの途中でパッケージマネージャー自体が壊れる可能性がある。ライブラリのバージョンが正しくないため、パッケージマネージャーが壊れている場合、ライブラリのロールバックやダウングレードは不可能である。通常の解決策は、両方のアプリケーションをダウンロードしてデプロイすることであり、一時的な環境内から行う場合もある。
パッケージマネージャの依存関係
パッケージマネージャ（APT など）を介して準備されたパッケージをインストールすると依存関係地獄が発生する可能性があるが、主要なパッケージマネージャは成熟しており、公式リポジトリが適切に管理されているため、依存関係地獄が発生する可能性は低い。これは、Debian の現在のリリースと、Ubuntu などの主要な派生の場合である。ただし、パッケージインストーラ（RPM または dpkg など）を介してパッケージを直接インストールすると、依存関係地獄が発生する可能性がある。
ダイヤモンド依存関係（Diamond dependency）
ライブラリ A がライブラリ B と C に依存し、B と C の両方がライブラリ D に依存するが、B はバージョン D.1 を必要とし、C はバージョン D.2 を必要とする場合、最終的な実行ファイルには D のバージョンが 1 つしか存在できないため、ビルドは失敗する。
yum などのパッケージマネージャーは、リポジトリのパッケージ間で競合が発生しやすく、CentOS や Red Hat Enterprise Linux などの Linux ディストリビューションで依存関係地獄を引き起こす。

 解決法 

依存関係の削除  
多くのソフトウェアライブラリは、ほとんどのユーザーのニーズを満たすように多くの機能を提供しているが、ホストコードではごく一部しか必要とされない場合がある。ソースを調べることで、機能をはるかにコンパクトな方法で書き直すことができる場合がある。一般に、これによりコードが大幅に削減され、後のメンテナンスコストが削減される。

バージョン番号付け
この問題に対する非常に一般的な解決策は、標準化された番号付けシステムを使用することである。このシステムでは、ソフトウェアは各バージョン（メジャーバージョンとも呼ばれる）に特定の番号を使用し、各リビジョン（マイナーバージョンとも呼ばれる）にサブ番号を使用する。メジャーバージョンは、そのバージョンを使用していたプログラムが互換性がなくなる場合にのみ変更される。マイナーバージョンは、他のソフトウェアの動作を妨ぐことがない単純なリビジョンで変更される。このような場合、ソフトウェアパッケージは、特定のメジャーバージョンと任意のマイナーバージョン（特定のマイナーバージョン以上）を持つコンポーネントを要求するだけで済む。そのため、マイナーバージョンが変更された場合でも、それらは引き続き機能し、依存関係は正常に解決される。セマンティックバージョニング（別名「SemVer」）は、特別にフォーマットされた番号を使用してソフトウェアのバージョン管理スキームを作成する技術仕様を生成する取り組みの一例である。

アプリケーションごとのプライベートバージョン  
Windows 2000で導入されたにより、アプリケーションがシステムDLLを上書きすることが防止された。開発者は、代わりに「プライベートDLL」、つまりアプリケーションごとのライブラリのコピーをアプリケーションのディレクトリで使用するよう推奨された。これは、ローカルパスが常にシステムディレクトリよりも優先されるというWindows検索パスの特性を利用している。これにより、特定のライブラリバージョンを簡単かつ効果的にシャドウイングできるため、依存関係地獄を回避できる。  
TrueOS（FreeBSDベースのオペレーティングシステム）の前身であるPC-BSDは、バージョン8.2まで、パッケージと依存関係を内の自己完結型ディレクトリに配置していた。これにより、システムライブラリがアップグレードまたは変更された場合の問題を回避できる。パッケージ管理には独自の「PBI」（プッシュボタンインストーラ）を使用していた。

複数バージョンの並列インストール  
バージョン番号付けは、オペレーティングシステムでサポートされることにより改善される。これにより、アプリケーションは一意の名前とバージョン番号を指定してモジュール/ライブラリを要求できるようになり、ライブラリ/モジュールのバージョンを仲介する責任がアプリケーションからオペレーティングシステムに効果的に移行される。共有モジュールは、モジュールの以前のバージョンまたはそれ以降のバージョンに依存するアプリケーションを壊すリスクなしに、中央リポジトリに配置できる。各バージョンは、同じモジュールの他のバージョンと並列に独自のエントリを取得する。  
この解決法は、Windows Vista以降のMicrosoft Windowsで使用されている。Windows Vistaでは、グローバルアセンブリキャッシュがこのような中央レジストリの実装であり、関連するサービスを備え、インストールシステム/パッケージマネージャと統合されている。Gentoo Linuxは、スロットと呼ばれる概念でこの問題を解決し、共有ライブラリの複数のバージョンをインストールできるようにする。

スマートパッケージ管理  
一部のパッケージマネージャはスマートアップグレードを実行できる。スマートアップグレードでは、相互に依存するソフトウェアコンポーネントが同時にアップグレードされるため、メジャー番号の非互換性の問題も解決される。  
現在の多くのLinuxディストリビューションでは、依存関係の問題を解決するためにリポジトリベースのパッケージ管理システムが実装されている。これらのシステムは、RPM、dpkg、またはその他のパッケージシステムの上位レイヤーであり、定義済みのソフトウェアリポジトリを検索して依存関係を自動的に解決するように設計されている。これらのシステムの例としては、APT、Yum、Urpmi、ZYpp、Portage、Pacmanなどがある。通常、ソフトウェアリポジトリは、FTPサイトまたはWebサイト、ローカルコンピュータ上のディレクトリ、ネットワーク経由で共有されているディレクトリ、それほど一般的ではないが、CDやDVDなどのリムーバブルメディア上のディレクトリである。これにより、これらのリポジトリでパッケージ化されたソフトウェアの依存関係地獄が解消される。これらのリポジトリは通常、Linuxディストリビューションプロバイダによって管理され、世界中にミラーリングされる。これらのリポジトリは巨大であることが多いが、すべてのソフトウェアをリポジトリに含めることは不可能であるため、依存関係地獄が発生することがある。いずれの場合も、リポジトリのメンテナーは依然として依存関係地獄に直面している。

インストーラオプション  
ソフトウェアごとに依存関係が異なるため、新しいパッケージごとにさらにいくつかのパッケージのインストールが必要になり、依存関係要件の悪循環、つまり要件の木構造が拡大し続ける可能性がある。DebianのAPTなどのシステムは、ユーザーにさまざまな解決策を提示し、ユーザーが必要に応じてそれを受け入れるか拒否できるようにすることで、この問題を解決している。  

プログラミングの容易な適応性  
プログラマがOS、ウィンドウマネージャ、またはデスクトップ環境を扱うインタフェイスレイヤーを新しい標準に簡単に適応できるようにアプリケーションが設計されている場合、プログラマは環境作成者またはコンポーネントライブラリ設計者からの通知を監視し、ソフトウェアをすばやく調整するだけで済む。すべて最小限の労力で、コストと時間のかかる再設計は必要ない。

コード開発と保守における厳格な互換性要件  
アプリケーションとライブラリが下位互換性の保証を念頭に置いて開発および保守されている場合、アプリケーションやライブラリはいつでも新しいバージョンに置き換え可能であり、何も壊れることはない。これにより、多数の依存関係が軽減されるわけではないが、パッケージマネージャやインストーラの作業が大幅に容易になる。

依存関係の問題を回避するもう一つの方法は、アプリケーションをソフトウェアアプライアンスとしてデプロイすることである。ソフトウェアアプライアンスは、依存関係を事前に統合された自己完結型のユニットにカプセル化するため、ユーザーはソフトウェア依存関係の解決について心配する必要がなくなる。代わりに、その負担はソフトウェアアプライアンスの開発者に移る。コンテナとそのイメージ（DockerやDocker Hubによって提供されるものなど）は、ソフトウェアアプライアンスの実装と見なすことができる。

ポータブルアプリケーション  
ポータブルアプリケーションとは、完全に自己完結型で、何もインストールする必要のないアプリケーション（または既存の従来型アプリケーションのバージョン）である。必要なコンポーネントがすべて含まれるようにコーディングされているか、必要なファイルがすべて独自のディレクトリ内に保持されるように設計されているため、依存関係の問題は発生しない。これらは、接続先のシステムとは独立して実行できる場合が多い。RISC OSおよび for Linuxのアプリケーションは、を使用する。これらは、ほぼ同じように機能する。プログラムとその依存関係は、独自のディレクトリ（フォルダー）内で自己完結する。  
この配布方法は、Unix系プラットフォーム用に設計されたアプリケーションをWindowsに移植する場合にも役立つ。最も顕著な欠点は、同じ共有ライブラリが複数インストールされることである。たとえば、gedit、GIMP、HexChatのWindowsインストーラにはすべて、これらのプログラムがウィジェットのレンダリングに使用するGTKツールキットの同一のコピーが含まれている。一方、各アプリケーションで異なるバージョンのGTKが必要な場合は、これが正しい動作であり、依存関係地獄をうまく回避できる。

プラットフォーム固有の問題
特定のプラットフォームでは、「依存関係地獄」はしばしば固有の名前で呼ばれ、その名前は通常、コンポーネントの名前に由来する。

 DLL地獄 – 16ビットのMicrosoft Windowsで発生する依存関係地獄。
 Extension conflict – Classic Mac OSで発生する依存関係地獄。
 JAR地獄 – Apache Mavenなどのビルドツールが2004年にこの問題を解決する前のJavaランタイム環境で発生する依存関係地獄。
 RPM地獄 – RPMを使用するディストリビューションで発生する依存関係地獄。

 脚注 

 関連項目 
 構成管理ソフトウェアバージョンを管理するためのツールやテクニック
 結合度
 パッケージマネージャ

 静的リンク

Category:パッケージ管理システム
Category:バージョン管理システム
Category:コンピュータのエラー
Category:ソフトウェア工学のフォークロア