Downloading and manually installing software one piece at a time doesn't scale — a package manager tracks what's installed, resolves dependencies automatically, and makes updating or removing software a single reliable command instead of a manual archaeology project.
3 min read
Installing a piece of software often means installing several other pieces of software it depends on first — and those dependencies can have their own dependencies. Tracking this by hand, ensuring compatible versions, and keeping everything updated without breaking something else is exactly the kind of bookkeeping a package manager automates.
Linux distributions and macOS each have their own system-level package
manager — Debian/Ubuntu use apt, macOS commonly uses the third-party
brew (Homebrew) — but the actual commands map onto the same handful of
concepts everywhere:
$ sudo apt update # refresh the list of available packages and versions
$ sudo apt install curl # install a specific package
$ sudo apt upgrade # upgrade every installed package to its latest version
$ sudo apt remove curl # uninstall a package$ brew install wget # Homebrew's equivalent install command
$ brew upgrade # Homebrew's equivalent upgradeapt update doesn't install anything at all — it only refreshes the local
list of what packages and versions are available. apt upgrade is the step
that actually installs newer versions of already-installed packages, based on
that refreshed list. Running upgrade without first running update risks
installing based on stale information about what versions even exist —
exactly why these are kept as two explicit, separate commands rather than
combined into one.
Installing one package often requires several others to already be present at compatible versions — a package manager resolves this dependency graph automatically, installing whatever's missing and flagging genuine conflicts (two packages requiring incompatible versions of the same dependency) rather than leaving you to work out the correct install order and versions by hand.
Beyond the operating system's own package manager, most programming languages
have their own, managing libraries specific to that language: pip for
Python, npm for JavaScript/Node.js. These solve the exact same underlying
problem (dependencies, versions, install/update/remove) at the level of a
single project's code dependencies rather than system-wide software:
$ pip install requests # a Python library, for this project/environment
$ npm install express # a Node.js library, for this projectManually downloading and installing software one piece at a time, from whatever website happens to host it, means no consistent way to check for updates, no automatic dependency resolution, and no easy way to see what's actually installed on a system at all. A package manager keeps a real, queryable record of what's installed — genuinely important on a server you didn't set up yourself, where knowing exactly what software is present (and from where) is part of even basic security hygiene.
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. Why are 'update' and 'upgrade' kept as two separate commands in apt?
2. What real problem does a package manager's dependency resolution solve?
3. Why do language-specific package managers like pip or npm exist alongside system package managers like apt?
Linux & the Command Line