Dependency Management
You are auditing or managing dependencies for: $ARGUMENTS
Dependencies are code you did not write that runs in your production service. Every dependency you add is a liability — a source of bugs, security vulnerabilities, and breaking changes. Treat adding a dependency as a decision that requires justification, not a default action.
Principles
- Every dependency must justify its existence. Does this do something that would take more than a day to write yourself? No? Write it yourself.
- Pin major versions; allow auto-updates for patches.
^1.2.3is reasonable;*orlatestis not. - Vulnerability scanning is not optional. Run it in CI; block merges with CRITICAL/HIGH CVEs.
- Dead dependencies are deleted, not commented out. If you are not using it, remove it.
- Update regularly, not desperately. Monthly minor/patch updates are routine. Emergency patch updates at 11pm are what happens when you ignore months of CVE alerts.
1. Audit Current State
# Node.js — find vulnerabilities
npm audit
npm audit --audit-level=high # non-zero exit on HIGH or CRITICAL
# Find unused packages
npx depcheck
# Visualise package sizes
npx bundlesize
npx cost-of-modules
# Python
pip-audit
safety check
# PHP
composer audit
# Ruby
bundle audit check --update
# Go
govulncheck ./...
# Rust
cargo audit
2. Version Pinning Strategy
Node.js (package.json)
{
"dependencies": {
"express": "^4.18.2", // OK: allow patch + minor bumps within major 4
"pg": "~8.11.3" // Tighter: allow patch updates only
},
"devDependencies": {
"typescript": "^5.4.0",
"jest": "^29.0.0"
}
}
Always commit package-lock.json (Node) or yarn.lock. This pins the exact transitive tree.
# Reproducible install from lockfile (CI)
npm ci # always use ci, not install, in CI pipelines
# Update with intent
npm update express # update one package
npx npm-check-updates # see what's outdated
npx npm-check-updates -u && npm install # apply all updates (review diff first!)
Python (requirements.txt / pyproject.toml)
# pyproject.toml — pin in ranges
[tool.poetry.dependencies]
python = "^3.12"
fastapi = "^0.110"
sqlalchemy = "^2.0"
# For production deploy: freeze the exact transitive tree
poetry export -f requirements.txt --without-hashes > requirements.lock
# Then use requirements.lock in Dockerfile:
COPY requirements.lock .
RUN pip install --no-cache-dir -r requirements.lock
PHP (composer.json)
{
"require": {
"laravel/framework": "^11.0",
"league/flysystem": "^3.0"
}
}
composer install --no-dev # production install from composer.lock
composer update package/name # update one package
composer audit # check for known vulnerabilities
3. Evaluating a New Dependency
Before running npm install some-package, answer:
| Question | Green | Red |
|---|---|---|
| Maintenance | Last commit < 6 months ago | Last commit > 2 years ago |
| Activity | Open PRs reviewed regularly | 100+ open issues, no responses |
| Downloads | Widely used (check npm/PyPI stats) | < 100 downloads/week |
| Vulnerabilities | 0 known CVEs | Known unpatched vulnerability |
| Bundle size | < 10kb (for browser bundles) | Pulls in 500kb of transitive deps |
| License | MIT, Apache-2.0, BSD | GPL (copyleft), AGPL, no license |
| Alternatives | Is this the standard solution? | Multiple competing packages for same job |
# Check a package before installing
npm info some-package | grep -E "version|license|dependencies|deprecated"
# Check download stats
open https://npmtrends.com/some-package
# Check bundle size impact
open https://bundlephobia.com/package/some-package
4. Automated Dependency Updates
Use Dependabot or Renovate to receive automated PRs for updates:
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
day: monday
open-pull-requests-limit: 10
groups:
dev-dependencies:
patterns: ["*"]
dependency-type: development
versioning-strategy: auto
- package-ecosystem: docker
directory: /
schedule:
interval: weekly
Review automated PRs before merging:
- Check the changelog for breaking changes
- Ensure CI passes (don't just merge green)
- Review if a minor version bump includes a security fix — prioritise those
5. Removing Unused Dependencies
# Node.js
npx depcheck
# Review output: "Unused dependencies" and "Missing dependencies"
# Remove package and update lockfile
npm uninstall some-package
# Python
pipreqs . --force # regenerate requirements from imports
# Compare with current requirements.txt
# PHP
composer why package/name # see what requires this package
6. License Audit
Not all open-source licenses are compatible with commercial use:
# Node.js
npx license-checker --summary
npx license-checker --exclude "MIT;Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC" --failOn GPL
# Python
pip-licenses --format=markdown
# PHP
composer licenses
License risk:
- Safe: MIT, Apache-2.0, BSD-2/3-Clause, ISC
- Review: LGPL (permitted if dynamically linked), MPL-2.0
- Risky: GPL-2.0, GPL-3.0 (viral — may require your code to be GPL too)
- Commercial only: AGPL-3.0 (any network use must be open-sourced)
7. Monthly Dependency Maintenance
Run this every month:
# 1. Security audit — fail on HIGH/CRITICAL
npm audit --audit-level=high
# 2. Outdated packages
npm outdated
# 3. Update patch versions (low risk)
npm update
# 4. Review Dependabot/Renovate open PRs — merge or close them
# 5. Check for deprecated packages
npm ls 2>&1 | grep "deprecated"
# 6. Remove anything that's no longer imported
npx depcheck
8. Checklist
- [ ]
package-lock.json/yarn.lock/composer.lockcommitted - [ ] CI runs
npm audit --audit-level=highor equivalent and fails on CRITICAL/HIGH - [ ] No packages on
latestor*versions - [ ] Dependabot or Renovate configured for automatic update PRs
- [ ] License audit run — no GPL dependencies in a commercial product
- [ ] No unused dependencies (run
depcheck) - [ ] No deprecated packages (check npm warnings)
- [ ] New dependencies documented with rationale in
docs/dependencies.md(for non-obvious ones)