In software, the meaning of dependencies is simple: a dependency is any external piece of code — a library, package, module, or framework — that your project relies on to function. When you run npm install, pip install, or add a line to a pom.xml, you are declaring a dependency: telling your build system to fetch someone else's code and make it part of your application. Almost no modern software is written entirely from scratch. A typical application is mostly other people's code, stitched together by a comparatively small amount of your own, and understanding what that means is the starting point for both productivity and security.
The concept is easy; the consequences are not. The reason "dependencies" is a security topic at all is that when you depend on code, you inherit its behavior, its bugs, and its vulnerabilities — usually without reading a line of it.
What counts as a dependency?
Anything your code needs at build time or run time to work. That includes the obvious cases: a web framework like Express or Spring, a utility library like Lodash, a database driver, a JSON parser. It also includes less visible things: build tools, test frameworks, code generators, and the compiler or runtime itself. Package managers formalize all of this by letting you declare dependencies in a manifest file — package.json for npm, requirements.txt or pyproject.toml for Python, pom.xml or build.gradle for Java — and then resolving and downloading them automatically.
The manifest is the intent. The lockfile — package-lock.json, poetry.lock, Gemfile.lock — is the record of exactly which versions were actually installed, down to the specific release and hash. That distinction matters enormously for security and reproducibility, because the manifest often says "any 4.x version" while the lockfile pins the exact one that shipped.
What is the difference between direct and transitive dependencies?
A direct dependency is one you explicitly asked for — the package you named in your manifest. A transitive dependency is one that got pulled in because a direct dependency needed it, and so on down the chain. You chose Express; Express chose its own dependencies; those chose theirs. You end up running all of them.
This is where the numbers get startling. It is common for a project with a dozen direct dependencies to end up with several hundred transitive ones. A single create-react-app scaffold has historically pulled in well over a thousand packages, the vast majority of which the developer never named and never reviewed. The meaning of a dependency, then, is broader than most people assume: it is not just what you asked for, but the entire tree that request unfolds into.
Transitive dependencies are where most supply chain risk hides, precisely because they are invisible in the manifest. A critical vulnerability three levels deep is still running in your application, and package.json gives no hint that it is there.
Why do dependencies matter for security?
Because you inherit everything about the code you depend on. If a library has a vulnerability, your application has that vulnerability the moment you include it. If a maintainer's account is compromised and a malicious version is published, you can pull that malicious code on your next build without any action on your part. The infamous cases — a popular package handed to a bad actor, a compromised publish token pushing a trojaned release — all worked because dependencies are trusted by default and executed automatically.
The install step itself is a risk in some ecosystems. npm packages can run scripts during installation, which means malicious code can execute the instant you install, before your application even starts. This is why "just add the package" is never quite as free as it looks, and why knowing your full dependency tree — not just the top of it — is a security requirement, not a nicety.
How do you keep dependencies under control?
The foundation is knowing what you have. You cannot secure a tree you cannot see, so the first step is generating an accurate inventory of every direct and transitive dependency, ideally as a software bill of materials (SBOM) produced automatically on each build. From there, a few disciplines carry most of the weight.
Pin versions with a lockfile and use the strict install command (npm ci, not npm install) in CI so builds are reproducible and a swapped tarball cannot slip in. Scan the full tree against vulnerability databases so you learn about a critical flaw in a transitive package before an attacker does — this is exactly what software composition analysis does, and the good tools go further by telling you whether the vulnerable code is actually reachable from your application, so you fix what matters instead of chasing every advisory. Keep dependencies reasonably current, because a wildly out-of-date tree accumulates known vulnerabilities that are trivial to exploit. And prune what you do not use; a dependency you no longer need is pure risk with no benefit. These habits form the core of practical dependency management training.
Does more dependencies always mean more risk?
More dependencies mean more surface, but the relationship is about awareness, not raw count. A large, well-inventoried, actively maintained tree that you scan and update is far safer than a small one you never look at. The danger is not that you use other people's code — that is unavoidable and usually correct — but that you use it without knowing what is in the tree, whether it is maintained, and whether it carries known flaws. The meaning of dependencies, in the end, is a trade: you get speed and reuse, and you take on the responsibility of knowing and managing what you brought in.
FAQ
What does "dependencies" mean in programming?
A dependency is external code — a library, package, module, or framework — that your project needs in order to build or run. Declaring a dependency tells your package manager to fetch that code and include it in your application.
What is the difference between direct and transitive dependencies?
A direct dependency is one you explicitly listed in your manifest. A transitive dependency is one pulled in automatically because a direct dependency (or another transitive one) requires it. Most of your dependency tree is usually transitive.
Why are transitive dependencies a security concern?
They run in your application just like direct ones but are invisible in your manifest, so vulnerabilities buried several levels deep are easy to miss. Most dependency-related supply chain risk lives in the transitive layer.
How many dependencies is too many?
There is no fixed number. What matters is whether you have visibility into the full tree, keep it updated, and scan it for known vulnerabilities. A large tree you manage well is safer than a small one you ignore.