A dependency in programming is any external piece of code, usually a library or package, that your software needs in order to build or run correctly. When you import a date-formatting library, a web framework, or a logging package instead of writing that functionality yourself, you have created a dependency. Modern applications have hundreds or thousands of them, and understanding what they are is the first step to understanding why software supply chain security exists as a discipline.
The basic idea
Programmers rarely write everything from scratch. If you need to parse JSON, make HTTP requests, or hash a password, someone has already written and tested code that does it well. You pull that code in as a dependency and call it, instead of reinventing it.
You declare dependencies in a manifest file. The exact file depends on the ecosystem:
JavaScript / Node.js package.json
Python requirements.txt or pyproject.toml
Java (Maven) pom.xml
Ruby Gemfile
Rust Cargo.toml
Go go.mod
A package manager reads that manifest, downloads the declared packages from a registry, and places them where your program can find them. In Node.js that registry is npm; in Python it is PyPI; in Java it is Maven Central.
Direct versus transitive dependencies
This distinction is the one that trips people up, and it is the one that matters most for risk.
A direct dependency is a package you explicitly asked for in your manifest. A transitive dependency is a package that your direct dependencies depend on, and so on down the chain. You never asked for them by name, but they end up in your application anyway.
The numbers get large fast. You might list 20 direct dependencies. Each of those pulls in its own dependencies, which pull in theirs, and the fully resolved tree can easily reach several hundred or several thousand packages. The overwhelming majority are transitive, meaning most of the code shipping in your application is code you never directly chose.
You can see the shape of this tree with tooling:
# Node.js
npm ls --all
# Python
pip install pipdeptree && pipdeptree
# Maven
mvn dependency:tree
Why dependencies are a security concern
Every dependency, direct or transitive, is code you run with the same privileges as your own. That has three consequences.
Known vulnerabilities. A package you depend on may contain a flaw with a published CVE. When a widely used library has a serious vulnerability, every application depending on it, often transitively, is exposed until it updates. Teams frequently do not even know they include the affected package because it arrived several levels deep.
Malicious packages. Attackers publish packages designed to do harm, sometimes as typosquats of popular names, sometimes by compromising a legitimate maintainer's account. Because install-time scripts can execute code, a malicious dependency can run on your build machine before your program even starts.
Maintenance and licensing risk. A dependency can be abandoned, leaving vulnerabilities unpatched, or carry a license incompatible with how you ship your software. Both are risks that surface only when you look at the full tree.
The core problem is trust by proxy. You vetted your 20 direct dependencies, maybe. You almost certainly did not vet the 800 transitive ones, yet you run all of them.
How teams manage dependency risk
The practices that keep a dependency tree healthy are well established:
- Lockfiles. Files like
package-lock.jsonorpoetry.lockpin the exact resolved versions so builds are reproducible and a surprise version cannot slip in. - Software Bill of Materials (SBOM). An SBOM is a machine-readable inventory of every component in your application, direct and transitive. It is what lets you answer "are we affected by this new CVE?" in minutes instead of days.
- Software composition analysis. SCA tooling scans your resolved tree against vulnerability databases and flags known-bad packages, including deep transitive ones. An SCA tool such as Safeguard can flag a vulnerable package even when it sits several layers below your direct dependencies.
- Regular updates. Staying reasonably current means you inherit upstream security fixes instead of accumulating years of unpatched debt.
If you want to see how these ideas play out in a real registry, the npm-specific mechanics in the npm prepare script guide show how a single install can execute third-party build code.
A concrete mental model
Picture your application as the top of a pyramid. The wide base is thousands of lines of other people's code that you inherited by declaring a handful of packages. Most of that base is invisible to you day to day. Supply chain security is the practice of making that base visible and keeping it healthy, because a crack anywhere in it can bring down what you built on top.
That is why "what is a dependency in programming" is not a beginner-only question. Senior engineers and security teams spend real effort on exactly this, because the answer, once you include the transitive layers, is "far more code than you think, most of which you did not choose."
FAQ
What is the difference between a direct and a transitive dependency?
A direct dependency is one you explicitly list in your manifest. A transitive dependency is one pulled in by your direct dependencies, and by their dependencies in turn. Most packages in a resolved tree are transitive, meaning you did not choose them by name but still run them.
Are dependencies bad for security?
Dependencies are not inherently bad; they save enormous effort and are usually well tested. The risk is that each one is code running with your privileges, so a vulnerable or malicious package becomes your problem. The goal is to manage that risk with inventories, scanning, and updates, not to avoid dependencies entirely.
What is a dependency manifest?
A manifest is the file where you declare the packages your project needs, such as package.json for Node.js, requirements.txt for Python, or pom.xml for Maven. A package manager reads it and installs the listed packages plus their transitive dependencies.
How do I see all the dependencies my project uses?
Use your ecosystem's tree command, such as npm ls --all, pipdeptree, or mvn dependency:tree. For a durable inventory that security tooling can consume, generate an SBOM, which lists every direct and transitive component in a machine-readable format.