Almost no modern software is written from scratch. Most projects glue together hundreds — sometimes thousands — of small libraries written by other people. The program that downloads, organises, and updates those libraries for you is called a package manager. Once you understand the pattern, the dozens of variants are all the same idea with different names.
The pattern
A package manager has three jobs:
- Discover."Is there a library called X? What versions exist?" The package manager reads from a public registry — a giant online catalogue.
- Install."Download library X version 2.4 into this project, along with everything it needs to work." That last clause is sneaky: most libraries depend on other libraries, which depend on more libraries. The package manager works out the whole tree and downloads all of it.
- Track."Write down what we installed and at which version so anyone else who clones this project can reproduce the exact setup."
Every modern language has a package manager built around this pattern. The names change, the tools change, the basic shape doesn't.
The big ones you'll meet
- npm (Node Package Manager) — for JavaScript. The biggest registry in the world. Often replaced or augmented by pnpm or yarn for performance, but they all talk to the same registry.
- pip — for Python. Often used alongside
venvoruvto manage isolated environments per project. - cargo — for Rust. Beautifully designed; many others now copy its approach.
- brew, winget, apt — system-level package managers for installing applications rather than libraries. Same pattern, different scope.
In this curriculum you'll spend most of your time with npm, because Claude Code itself is installed via npm and most of the example projects we'll build with it use JavaScript.
What it looks like in practice
# Inside a project folder, add a library called "lodash" npm install lodash # Install everything the project says it depends on npm install # Remove a library npm uninstall lodash # Update libraries to newer versions (within allowed ranges) npm update
That's the entire common interface. Equivalent commands exist for pip, cargo, etc.
The two files that record everything
In a JavaScript project, two files document the dependency setup:
package.json— what your project says it needs. Library names and acceptable version ranges. You edit this (directly or vianpm install ...).package-lock.json— what was actuallyinstalled, down to the exact version of every dependency-of-a-dependency. You don't edit this; npm writes it.
The lock file is what makes a project reproducible. Two developers running npm installfrom the same lock file get the exact same versions of everything. Without it, you'd get "works on my machine" bugs constantly.
Every language's package manager has some version of these two files. For Python: requirements.txt or pyproject.toml + a lock file. For Rust: Cargo.toml and Cargo.lock.
Where do the libraries actually live?
npm puts everything in a folder called node_modules inside your project. Open it up and you'll find hundreds, often thousands, of small sub-folders. Each one is a library someone else wrote.
This folder gets huge fast. A modest web project can easily have a node_modulesof several hundred megabytes. That's normal and expected. Never commit it to git. You commit package.json and package-lock.json; anyone can recreate node_modules with npm install.
That's what the file .gitignoreis for — it lists files and folders that git should pretend don't exist. node_modules belongs in there, always.
Version numbers
Package versions usually follow a pattern called semantic versioning: 2.4.7. Three numbers, separated by dots:
- Major (the 2) — bumped when the library makes breaking changes. Old code may not work with the new version.
- Minor(the 4) — bumped when new features are added that don't break existing code.
- Patch (the 7) — bumped for bug fixes.
In package.json you'll see ranges like "^2.4.7". The caret means "accept new minor and patch versions but not a new major." Most of the time, this is fine and you can ignore it.
Security and the "left-pad" problem
You will be installing code written by strangers. Sometimes that code is actively malicious. Sometimes it's benign but abandoned. Sometimes it gets compromised after it was already popular.
The defensive habits:
- Prefer popular, well-maintained libraries to obscure ones doing the same thing.
- Look at the number of downloads, the date of the last commit, and the number of open issues before installing.
- Don't install random one-off libraries when you only need a few lines of code.
- Run
npm auditoccasionally — it warns about known security issues in your installed packages.
How Claude Code interacts with package managers
Claude Code is fluent in every common package manager. When you ask it to add a feature that needs a new library, it'll usually:
- Suggest a specific library (and tell you why it picked that one).
- Run the install command (with your permission).
- Update
package.jsonand the lock file automatically. - Write the actual code that uses it.
Your job is to glance at the library it picked. If you don't know it, ask Claude to tell you about it before installing. Two minutes of due diligence prevents the random-stranger-on-the-internet problem from sneaking into your codebase.
- A package manager downloads, installs, and tracks the libraries your project depends on.
package.jsonsays what you want;package-lock.jsonrecords what you got. Both belong in git;node_modulesdoes not.- Semantic versioning (
major.minor.patch) tells you what to expect from an upgrade. - Every dependency is code that runs with your permissions. Pick popular, maintained libraries and audit occasionally.