You declare a handful of names and version ranges. A resolver turns that into hundreds of packages you never chose, downloads them, arranges them on disk, and runs some of their code. Almost every confusing failure here lives in the gap between what you declared and what you got: the build that worked yesterday, the library loaded twice, the import that resolves on your laptop but not on the build server. This guide is about where each decision is made and which file controls it. Once you can say which step chose a version, the fix is usually one line.
Each chapter opens with the short version. Tap one to read the detail.
What a package actually is
~2 min
A package is a folder with a package.json, shipped as one tarball under a name@version whose contents can never be rewritten. What goes into that tarball is decided by the manifest, not by what your repository tracks.
Dependency kinds, and why the wrong one breaks a consumer
~2 min
dependencies are installed transitively by everyone who installs you. devDependencies never leave your repository. Filing something under the wrong one is the classic library bug, and it is invisible on your own machine because your machine has both.
What a version number promises, and what a range asks for
~2 min
The three parts are a publisher's claim — a major may break you, a minor adds, a patch fixes — and nothing enforces it. A range turns that into "the newest release that fits", which is why an unchanged manifest can install different code on two days.
The lockfile is what makes an install reproducible
~2 min
A range says what is acceptable; the lockfile records what you actually got — exact version, tarball location and integrity hash for every package in the tree. Commit it. An ordinary install may rewrite it; a frozen install obeys it.
node_modules, hoisting, and the tree you did not choose
~2 min
Lookup is simple: a bare name is searched for in the nearest node_modules, then each parent directory, up to the root. Everything strange about installed trees follows from that — packages get lifted to the top so one copy serves many dependents.
Peer dependencies: one copy, shared
~2 min
A peer dependency says "I plug into the copy you already have", so the host supplies the single instance. Two packages demanding incompatible host ranges is a real conflict, and the flags that silence it install a pair the publisher says does not work.
Scripts, executables, and code that runs on install
~2 min
npm run puts node_modules/.bin on the path, so a script calls this project's copy of a tool by bare name. Lifecycle hooks go further: installing a package can execute its code on your machine, with your privileges.
Supply chain: what you are actually trusting
~2 min
Your trust boundary is the entire transitive tree, not your direct list. The recurring incident is a trusted name shipping a patch with a hostile install script. Auditing reports known advisories, which is useful and is not the same thing as safety.
Entry points, and the two module systems
~2 min
The exports field decides what a consumer may import: a file sitting in the tarball that exports does not list is unreachable. Conditions can send import and require to different builds of one library.
Monorepos, and the manager you install with
~2 min
Workspaces make sibling folders resolve as though they were published: one install, one lockfile, and an edit in one package is immediately live in another. Which manager you use changes the on-disk layout and what your code can accidentally import.
The build step: bundling, shaking and targets
~2 min
A bundler walks the module graph from an entry point and emits fewer files. How much it can delete depends on whether your modules are statically analysable and whether packages told the truth about side effects.
Written by Keentune. We are not affiliated with or endorsed by the organizations whose documentation informs this guide, and any linked sources belong to their respective owners.
All exam, test, and product names and trademarks are the property of their respective owners and are used here for identification and reference only. Keentune is independent study practice — not affiliated with, authorized, or endorsed by any of these organizations.