Dependency management
Packages, lockfiles, semantic versioning, supply-chain risk, and upgrades.
Dependencies are borrowed code plus borrowed risk. Good dependency management keeps builds reproducible, upgrades intentional, and supply-chain surprises boring.
The big idea
Every package you install becomes part of your production system.
Treat package changes like code changes. Review them, test them, and know how to roll them back.
Manifest vs lockfile
- Manifest
package.json,pyproject.toml,go.mod. The direct dependencies and allowed ranges.- Lockfile
package-lock.json,pnpm-lock.yaml,uv.lock,poetry.lock. The exact resolved graph.- Direct dependency
- A package your code imports directly.
- Transitive dependency
- A package pulled in by another package.
Applications should usually commit lockfiles. Libraries often avoid strict app-style lockfiles for consumers, depending on ecosystem conventions.
Semantic versioning
SemVer is MAJOR.MINOR.PATCH:
- Major: breaking changes.
- Minor: backward-compatible features.
- Patch: backward-compatible fixes.
Version ranges are promises plus trust. ^1.2.3 accepts many future versions in the same
major line. That is useful for fixes, but it means the lockfile is what makes installs
repeatable.
Add packages slowly
Before adding a package, ask:
Is the problem core or tiny?
A 10-line helper may be safer than a dependency tree with 40 packages.
Is it maintained?
Check recent releases, issue health, docs, and ecosystem adoption.
What does it run?
Be extra cautious with packages that execute scripts during install or build.
Can we remove it later?
Wrap large dependencies at the boundary so the whole app does not depend on their API.
Upgrade safely
npm outdated
npm audit
npm update <package>
npm test
npm run buildFor Python:
uv lock --upgrade-package fastapi
uv run pytestDo not blindly upgrade the whole world during an unrelated feature. Small upgrade PRs are easier to review and roll back.
Supply-chain basics
- Pin tool versions in CI.
- Commit lockfiles for applications.
- Use Dependabot/Renovate with tests.
- Remove unused packages.
- Paste random install commands into production images.
- Ignore install scripts in sensitive environments.
- Mix feature work and giant dependency upgrades.
- Keep abandoned packages forever because "it still works."
In practice
Pick one project and explain its dependency graph: package manager, manifest, lockfile, install command, build command, and upgrade workflow. If you cannot reproduce a clean install from those files, the project is not fully buildable.
Key takeaways
- Dependencies are production code you did not write, so review them deliberately.
- The manifest describes allowed ranges; the lockfile records the exact resolved graph.
- Commit application lockfiles to make builds reproducible.
- Upgrade in small batches with tests and build checks.
- Prefer fewer dependencies unless the package removes real complexity or risk.
Checkpoint questions
Use these to test whether the lesson is clear enough to explain without rereading.
- 1Why should applications commit lockfiles, and what risk do they reduce?
- 2What is the difference between a direct dependency and a transitive dependency?
- 3How would you upgrade a vulnerable package without blindly breaking production?
- 4What should you check before adding a new package to a small service?
References
External resources for going deeper after the lesson above.