Safeguard
Dev Practices

What Are Dependencies in Software?

A plain-English definition of software dependencies, how direct and transitive dependencies differ, and why most projects ship far more third-party code than code their own team wrote.

Safeguard Team
Product
6 min read

Dependencies meaning, in plain terms: a software dependency is any external package, library, or module that your code needs in order to build or run. If your application imports it, links against it, or calls it at runtime, and your team did not write it, it is a dependency. Modern applications are mostly assembled from these building blocks, which is why a typical web service contains far more third-party code than first-party code.

That single sentence answers the definition question. The practical questions, how dependencies get into your project, how deep the chain goes, and what risk they carry, take a little longer.

Dependencies Meaning: What Counts as a Dependency?

When developers ask about dependencies meaning, they usually want to know what the term actually covers. It is broader than most people expect:

  • Libraries and packages installed through a package manager: express from npm, requests from PyPI, spring-boot-starter-web from Maven Central.
  • Frameworks your application is built on, such as React, Django, or Rails.
  • Runtime platforms like the Node.js runtime or the JVM, without which your code cannot execute.
  • System libraries pulled in by your operating system or container base image, such as openssl or glibc.
  • Build and dev tooling like compilers, bundlers, linters, and test frameworks. These do not ship to production, but they run with full access to your source code during builds.

A useful mental model: your application is the top of a pyramid, and everything underneath it that you did not author is a dependency. Remove any layer and something stops compiling, starting, or working correctly.

How Do Direct and Transitive Dependencies Differ?

A direct dependency is one you chose. It appears in your manifest file: package.json, requirements.txt, pom.xml, go.mod, or Gemfile. You added it deliberately because you needed its functionality.

A transitive dependency is one your dependencies chose. Install one direct npm package and it brings its own dependencies, which bring theirs, and so on. The full tree for a single front-end framework can easily run into hundreds of packages. In most ecosystems, transitive dependencies outnumber direct ones by a factor of five to ten.

This distinction matters because responsibility does not stop at the manifest. A vulnerability three levels deep in the tree runs inside your process with the same privileges as code you wrote. The 2021 Log4Shell incident made this concrete: thousands of teams were exploitable through log4j-core even though many had never added it directly. It arrived transitively through logging facades and framework starters.

How Do Package Managers Resolve Dependencies?

Package managers do three jobs: they fetch packages from a registry, resolve version constraints across the whole tree, and record the result.

Version constraints are usually expressed as ranges. In npm, ^4.17.0 means any 4.x release at or above 4.17.0. The resolver walks the tree, finds versions that satisfy every constraint simultaneously, and produces a concrete set.

The record of that concrete set is the lockfile: package-lock.json, poetry.lock, Gemfile.lock, go.sum. Lockfiles pin exact versions and integrity hashes so that every developer and every CI run installs the identical tree. Committing your lockfile is one of the cheapest reliability and security wins available. Without it, two builds of the same commit can resolve to different code.

Why Do Dependencies Matter for Security?

Every dependency is code you execute but did not review. Three risk categories follow from that:

  1. Known vulnerabilities. Open source packages accumulate CVEs like any other software. If you never update, your exposure only grows. Software composition analysis (SCA) tools exist specifically to map your dependency tree against vulnerability databases; our SCA product page explains how that matching works in practice.
  2. Malicious packages. Attackers publish typosquats, take over maintainer accounts, or inject payloads into legitimate releases. The event-stream incident in 2018 and the ua-parser-js hijack in 2021 both delivered malware through packages with millions of weekly downloads.
  3. Abandonment. A package with no maintainer receives no security fixes. The code still works until the day a vulnerability is found, and then nobody is coming to patch it.

None of this is an argument against using dependencies. Writing your own cryptography or HTTP parsing is almost always riskier than depending on a well-maintained library. The argument is for treating the dependency tree as part of your codebase: inventoried, monitored, and updated.

How Do You Keep Dependencies Under Control?

A workable baseline for most teams:

  • Commit lockfiles and install from them in CI (npm ci, not npm install).
  • Generate an SBOM (software bill of materials) so you know exactly what ships in each release.
  • Scan continuously, not just at release time. New CVEs are published against old versions of packages every day.
  • Update on a cadence. Small, frequent upgrades are dramatically cheaper than a once-a-year migration across ten major versions.
  • Audit before adding. Check maintenance activity, download counts, open issues, and the package's own dependency tree before taking on a new direct dependency.
  • Prune what you do not use. Every removed package is attack surface you no longer carry.

Teams that want to go deeper on dependency risk can work through the supply chain modules in our Academy, which cover SBOMs, lockfile hygiene, and vulnerability triage step by step.

FAQ

What are dependencies in programming?

Dependencies are external pieces of software, such as libraries, packages, frameworks, and runtimes, that a program requires to build or run. They are declared in a manifest file and installed by a package manager rather than written by the project's own developers.

What is a transitive dependency?

A transitive dependency is a package required by one of your dependencies rather than by your own code. Your project depends on it indirectly, through the dependency tree, and it carries the same runtime privileges as any direct dependency.

How many dependencies does a typical project have?

It varies by ecosystem. A modest JavaScript application commonly resolves several hundred packages once transitive dependencies are counted, while Go and Rust projects tend to have leaner trees. The direct list in your manifest is almost always a small fraction of the full tree.

What is dependency hell?

Dependency hell describes situations where version constraints conflict: package A needs library X version 1, package B needs version 2, and the resolver cannot satisfy both. Lockfiles, semantic versioning discipline, and regular incremental upgrades are the standard ways to avoid it.

Never miss an update

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