A package manager automates declaring, resolving, fetching, and pinning the external libraries a project depends on, using lockfiles to make dependency resolution reproducible.1
What it does
Almost no project is self-contained — you stand on libraries written by others. A package manager (Go modules, Cargo, npm, pip, and so on) records what your project needs in a manifest and fetches it, including the transitive dependencies your direct dependencies pull in.1 The libraries you depend on expose their capabilities through an API, and the package manager’s job is to get a compatible set of them onto your machine and, with the build system, into your program.
Lockfiles and reproducibility
A subtlety lurks here: a requirement like “version 1.x” can resolve to different exact
versions over time, the classic “works on my machine” problem. The fix is a lockfile
(go.sum, Cargo.lock, package-lock.json, poetry.lock) that pins the exact version
— often with a checksum — of every dependency, direct and transitive. Commit the lockfile,
and everyone, plus every CI/CD run, installs a byte-for-byte
identical dependency tree. That is the foundation of a reproducible build: without pinned
inputs you can’t have a reproducible output, which is why the lockfile lives in
version control alongside the code.
Versioning and trust
Dependency ranges and lockfiles both rest on semantic versioning: the version number tells the resolver (and you) whether an upgrade is a safe patch, a backward-compatible feature, or a breaking major change. Package managers also distribute your library outward — publishing a versioned package others can depend on — which is why a stable, well-versioned API matters as much for what you ship as for what you consume.
Sources
-
Package manager — Wikipedia, for dependency declaration, resolution, fetching, and lockfile-based reproducibility. ↩ ↩2