Safeguard
Security

What Is a Dependency in Programming?

A dependency is any external code your software relies on to run. Here is what that really means, how direct and transitive dependencies differ, and why the concept sits at the heart of software supply chain security.

Yukti Singhal
Platform Engineer
6 min read

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.json or poetry.lock pin 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.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.