CI/CD Course
Dependency Management
In this lesson
Dependency management is the practice of declaring, resolving, versioning, and securing the external libraries and packages that an application relies on. Almost every modern application depends on code it did not write — frameworks, utility libraries, database drivers, authentication packages. Dependency management is how a team keeps those external pieces consistent across every developer's machine, every CI runner, and every production environment — and how they stay informed when one of those pieces develops a known security vulnerability.
Dependencies and the Dependency Graph
Every package a project installs can itself depend on other packages. Those packages depend on others still. The result is a dependency graph — a tree of direct and transitive dependencies that can run hundreds of packages deep, even for a modest application. A Node.js project that declares ten direct dependencies in its package.json may end up with several hundred packages in node_modules once transitive dependencies are resolved.
This matters for CI/CD because the pipeline must resolve and install the entire graph on every run — or restore it from a cache if nothing has changed (as covered in Lesson 12). It also matters for security: a vulnerability in a transitive dependency your team has never heard of can still affect your application. You are responsible for the entire graph, not just the packages you explicitly declared.
The Restaurant Supply Chain Analogy
A restaurant does not grow its own vegetables — it orders from a supplier. But if that supplier sources from a farm with a contamination problem, the restaurant is affected whether it knew about the farm or not. Dependencies work the same way. Your direct packages are the supplier. Their dependencies are the farm. A vulnerability anywhere in the chain reaches your application, and the responsibility for knowing about it and responding to it sits with you.
Lock Files — The Contract for Reproducible Installs
A lock file is an automatically generated file that records the exact resolved version of every package in the dependency graph — direct and transitive — along with a cryptographic hash that verifies the package content has not been tampered with. It is the difference between "install the latest compatible version of this package" and "install exactly this version, verified against this hash."
In a CI/CD context, lock files are non-negotiable. Without one, two pipeline runs on the same commit can produce different dependency sets if a package author releases a new version between runs. With a lock file committed to version control and a clean install command that respects it, every pipeline run installs exactly what was tested — nothing more, nothing less.
Lock Files by Ecosystem
Dependency Installation in the Pipeline
Every pipeline run needs a complete, verified dependency install before the build and test steps can run. The install step is typically the most time-consuming part of the pipeline that is not actually doing useful work — which is why caching (covered in Lesson 12) matters so much here. But caching only helps if the install itself is deterministic, and that requires the right command.
The distinction between a standard install and a clean install is critical in CI. npm install will update the lock file if it finds a discrepancy. npm ci will fail if the lock file does not match — which is exactly the behaviour you want in a pipeline. A pipeline should never silently update dependencies. It should fail loudly, prompting a developer to review and commit the change intentionally.
Dependency Installation Step — GitHub Actions
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # Restore node_modules from cache if lock file unchanged
- run: npm ci # Fail if package-lock.json is out of sync — never silently update
- run: npm audit --audit-level=high # Fail if any high-severity vulnerabilities found in the graph
What just happened?
Dependencies were restored from cache or installed fresh using the exact versions in the lock file. Then npm audit scanned the entire dependency graph for known vulnerabilities, failing the pipeline if any high-severity issues were found — before a single line of application code was even compiled.
Security — Vulnerabilities in the Dependency Graph
Dependencies are one of the most common attack surfaces in modern software. The 2021 Log4Shell vulnerability affected thousands of applications not because they chose to use a vulnerable component, but because Log4j was a transitive dependency buried several layers deep in their dependency graphs — many teams did not even know it was there.
Every major package ecosystem now provides tooling to scan the dependency graph for known CVEs (Common Vulnerabilities and Exposures). Running this scan as a pipeline step — rather than a manual periodic check — means that a newly disclosed vulnerability in a dependency triggers a pipeline failure on the next commit, making the exposure window as short as possible. Tools like GitHub Dependabot can also open automated pull requests to update vulnerable dependencies, closing the loop without requiring manual intervention.
Dependency Security Tools by Ecosystem
--audit-level=high to fail only on high or critical findings.pip install or poetry install in the pipeline.Keeping the Dependency Graph Healthy Over Time
Dependencies go stale. A package that was safe and current when the project started may accumulate security advisories, deprecated APIs, or abandoned maintenance over time. The cost of updating a dependency is almost always lower when it is done continuously — one minor version at a time — than when it is deferred until a major version update is required years later.
The pipeline has a role here too. Scheduled dependency audit runs — using on: schedule in GitHub Actions — mean that a vulnerability disclosed on a Tuesday will surface in the next nightly pipeline run rather than being discovered by a developer months later, or worse, by a security researcher. Pairing scheduled audits with Dependabot means many fixes arrive as automated PRs that go straight through the pipeline — reviewed, tested, and merged without requiring manual triage.
Warning: Excluding Lock Files from Version Control Breaks Every Pipeline That Follows
A .gitignore that excludes package-lock.json, poetry.lock, or any other lock file is one of the most damaging configuration choices a project can make. Without a committed lock file, every CI run resolves the dependency graph fresh from the registry — meaning two runs on the same commit can install different package versions if a new release appeared between them. The build becomes non-deterministic, vulnerability scanning becomes unreliable, and "it worked yesterday" becomes a genuine debugging statement. Lock files belong in version control. Always.
Key Takeaways from This Lesson
npm ci, yarn install --frozen-lockfile, and their equivalents fail loudly if the lock file is out of sync, rather than silently updating it.
npm audit or its equivalent on every CI run means newly disclosed CVEs surface immediately, not months later during a manual review.
Teacher's Note
Enable Dependabot on every repository from day one — the cost of setting it up is ten minutes; the cost of not having it is discovering a two-year-old vulnerability during a security audit the week before a launch.
Practice Questions
Answer in your own words — then check against the expected answer.
1. What is the term for the packages that your direct dependencies themselves depend on — the ones your project did not explicitly declare but still installs and is responsible for securing?
2. What npm command should be used in a CI pipeline instead of npm install — because it fails if the lock file is out of sync rather than silently updating it?
3. What is the name of the GitHub tool that automatically opens pull requests to update vulnerable or outdated dependencies — integrating with branch protection so fixes go through the full pipeline before merging?
Lesson Quiz
1. A pipeline runs twice on the same commit, three days apart. No code has changed. What does the lock file guarantee about the dependency install on both runs?
2. The 2021 Log4Shell vulnerability affected applications that had never directly declared Log4j as a dependency. What does this illustrate about dependency management?
3. A developer updates a package in package.json but forgets to run npm install to update the lock file before pushing. The pipeline runs npm ci. What happens?
Up Next · Lesson 14
Artifact Management
The build produces an artifact — but where does it go, how is it versioned, and how does the pipeline retrieve it for deployment? Artifact management is the answer.