This is the companion to Categorizing Package Registries, focusing on the client side: how package managers resolve dependencies, track versions, run build code, and declare dependencies. The data is also available as CSV. There are gaps; contributions welcome.

Each package manager combines these choices differently. Cargo uses backtracking resolution, generates lockfiles, allows build hooks via build.rs, and uses TOML manifests. Go uses minimal version selection, achieves reproducibility without lockfiles, forbids hooks entirely, and embeds dependencies in go.mod. npm uses deduplication with nesting, generates lockfiles, allows postinstall hooks, and uses JSON. The particular combination shapes the developer experience more than any single choice.

Resolution algorithms

How does the package manager decide which versions to install? The ecosyste.ms resolver documentation covers the major algorithm families.

SAT solving treats resolution as a satisfiability problem. Can find solutions when they exist and prove when they don’t, but computationally expensive. PubGrub is a variant that produces better error messages by tracking why versions were excluded.

  • Composer
  • DNF
  • Conda/Mamba1
  • Zypper1
  • opam2
  • pub3
  • Poetry3
  • uv3
  • pdm3
  • Swift Package Manager3
  • Hex3

Backtracking tries versions in order and backs up when conflicts arise.

  • pip
  • Cargo
  • Cabal4

ASP solving uses answer set programming for complex constraint solving.

  • Spack5

Minimal version selection picks the oldest version that satisfies constraints.

  • Go modules
  • vcpkg6

Deduplication with nesting installs multiple versions when packages need incompatible versions.

  • npm
  • Yarn
  • pnpm

Version mediation lets build systems pick winners using different strategies.

  • Maven7
  • Gradle8
  • NuGet9
  • sbt
  • Clojars10
  • Ivy

Molinillo is a backtracking solver with heuristics tuned for Ruby’s ecosystem.

  • Bundler
  • RubyGems
  • CocoaPods

System package resolution handles system-level constraints like file conflicts and provides/requires relationships.

  • apt11
  • pacman
  • apk
  • Portage
  • FreeBSD ports
  • pkgsrc

Single version per formula with topological sort for dependency ordering.

  • Homebrew

Explicit dependencies with no version resolution needed.

  • Nix
  • Guix

Lockfiles and reproducibility

Can you reproduce the same install later?

Generates lockfiles to record exact versions resolved. Committed to version control.

  • Bundler
  • npm
  • Yarn
  • pnpm
  • Cargo
  • Poetry
  • uv
  • pdm
  • Composer
  • Mix
  • pub
  • Swift Package Manager
  • Elm
  • Cabal12
  • Stack
  • Spack
  • Homebrew
  • NuGet
  • Julia
  • Conan
  • dub
  • CocoaPods
  • opam

Deterministic resolution through algorithms that produce stable results without needing a lockfile to pin versions.

  • Go modules13

No native lockfile means resolution happens fresh each time, or build systems handle pinning.

  • pip14
  • Maven
  • Gradle
  • apt
  • CRAN
  • Hackage15
  • CPAN
  • Conda16

Content-addressed packages are identified by input hash. Reproducibility without traditional lockfiles.

  • Nix
  • Guix

Build hooks

Can packages run code during installation?

Hooks allowed let packages execute scripts during install, build, or publish.

  • npm17
  • Yarn
  • pip18
  • Composer19
  • RubyGems20
  • Maven21
  • Gradle
  • Cargo22
  • CocoaPods
  • Homebrew
  • Nix23
  • apt/dpkg24
  • RPM/DNF

Hooks restricted allow some build-time execution with limitations.

No hooks by design.

  • Go
  • Elm
  • Swift Package Manager
  • pip28

Manifest format

How are dependencies declared?

TOML

  • Cargo
  • Poetry
  • uv
  • Julia
  • pdm

JSON

  • npm
  • Composer
  • Deno
  • Elm
  • dub
  • vcpkg

YAML

  • Conda
  • pub
  • Homebrew
  • GitHub Actions
  • Helm
  • pnpm

Host language

  • Bundler (Ruby)
  • CocoaPods (Ruby)
  • Gradle (Groovy/Kotlin)
  • sbt (Scala)
  • Mix (Elixir)
  • Swift Package Manager (Swift)
  • Leiningen (Clojure)
  • Nix

XML

  • Maven
  • NuGet
  • Ivy

Custom format

  • Go
  • Cabal
  • CPAN
  • CRAN
  • pip
  1. Via libsolv 2

  2. Uses external CUDF solvers. 

  3. Uses PubGrub 2 3 4 5 6

  4. Modular solver with backjumping. 

  5. Via Clingo

  6. Microsoft explicitly documents vcpkg’s minimal version selection algorithm

  7. Nearest definition wins. 

  8. Highest version wins. 

  9. Lowest applicable version. 

  10. Clojars uses Maven’s resolution algorithm since it’s a Maven-compatible repository. 

  11. Scoring with immediate resolution. 

  12. The cabal freeze command generates a freeze file pinning versions. 

  13. go.sum exists but contains checksums for verification, not version pins. MVS means the same go.mod always resolves to the same versions. 

  14. pip-tools, Poetry, and uv provide lockfile functionality for pip. 

  15. Cabal can generate freeze files, but Hackage itself doesn’t require them. 

  16. conda-lock is a separate tool that adds lockfile support. 

  17. postinstall scripts run after package installation. 

  18. Historically via setup.py; modern PEP 517 builds use wheel build backends, which still execute arbitrary code at build time. 

  19. Composer scripts can run at various lifecycle points. 

  20. Native extensions compile C code during installation. 

  21. Maven plugins execute during build phases. 

  22. build.rs scripts run at compile time, typically for native code compilation. 

  23. Build hooks run in a sandboxed environment. 

  24. System packages do have maintainer scripts that run as root, but those are part of the distribution’s build pipeline, not arbitrary user-space package hooks. 

  25. Scripts are disabled by default; must be explicitly enabled. 

  26. Lifecycle scripts are disabled by default. 

  27. Requires explicit permission flags for network, file system, and subprocess access. 

  28. Wheel format explicitly forbids install-time code execution; sdist still allows setup.py hooks.