Know what you build with
Map the dependency graph
Distinguish direct, transitive, development, build, and runtime dependencies and identify which code reaches production.
Your app runs code you wrote and code your dependencies chose. Start by seeing the full graph.
A direct dependency is a package you added to package.json yourself. A transitive dependency is a package one of your dependencies pulled in. You never picked it, but it runs with the same authority as everything else.
Print the whole tree:
npm ls --all
On a typical Node.js project this prints hundreds of lines. Ten direct dependencies often expand into several hundred transitive ones. Every line is code someone else can change.
When you want to know why a specific package is installed, ask npm to explain it:
npm explain qs
# [email protected]
# node_modules/qs
# qs@"6.11.0" from [email protected]
# node_modules/body-parser
# body-parser@"1.20.2" from [email protected]
Read the chain bottom-up: you installed express, which chose body-parser, which chose qs. If qs ships a malicious version, it arrived through a decision you never reviewed.
The tree is not the whole graph
Direct dependencies are only the first layer. Build plugins, CI actions in your workflow files, container base images, and binaries downloaded during install can all influence the artifact. None of them appear in npm ls.
Imagine an image upload service that imports one package. That package downloads a native image tool during installation. The downloaded binary never appears in the top-level manifest, but it still runs in production.
Record execution context, not just names
A dependency list without execution context is incomplete. Build tools can read CI secrets. Runtime packages can reach customer data. A test-only package that runs in CI still sees the job’s environment.
For each class of dependency, record two things: the path into the project, and the authority the component receives where it runs. devDependencies that never ship can still exfiltrate a CI token. That distinction drives everything else in this course.
Try this on your own project: generate the complete dependency tree and save the output. Pick one transitive package and show which direct dependency introduced it, where it runs, and whether removing the parent removes it. Then repeat the check for one build plugin or downloaded binary that the package tree does not show.
Lesson completed