Concept

Dependency Resolution

Definition

Dependency resolution is the algorithmic task of finding a set of package versions that simultaneously satisfies every constraint declared by a project and its transitive dependencies. Each package may require specific versions of other packages, and those packages have their own requirements, forming a graph that grows quickly with depth. A resolver searches this graph for a coherent assignment — every package present at exactly one version, and that version compatible with every requirement that mentions it.

The problem is harder than it sounds: it is in general NP-complete, equivalent to boolean satisfiability. Every package manager — apt, yum, npm, pip, cargo, gem, go modules — is, at its core, a SAT solver wearing a friendly command-line face.

Why it matters

How it works

A resolver begins with the direct dependencies declared in a project's manifest, then walks the graph of transitive dependencies, recording each constraint it encounters. When two paths through the graph demand different versions of the same package, the resolver attempts to find a version that satisfies both. If a compatible version exists, it picks one (usually the newest, sometimes the oldest, depending on policy) and continues; if none exists, the resolver either backtracks to try different choices upstream or reports an irreconcilable conflict.

Modern systems differ in strategy. Some, like Cargo, use the PubGrub algorithm to learn from each conflict and prune large parts of the search space. Others, like npm before version 7, used a greedy strategy that allowed multiple versions of the same package to coexist in nested directories — accepting bloat to avoid conflicts. The output is typically a lock file: a frozen, fully resolved version graph that future installs replay byte-for-byte, so that "works on my machine" stops meaning "happens to have picked a different version than yours."

Where it goes next

Continue exploring

Tags