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

  1. Every dependency must justify its existence. Does this do something that would take more than a day to write yourself? No? Write it yourself.
  2. Pin major versions; allow auto-updates for patches. ^1.2.3 is reasonable; * or latest is not.
  3. Vulnerability scanning is not optional. Run it in CI; block merges with CRITICAL/HIGH CVEs.
  4. Dead dependencies are deleted, not commented out. If you are not using it, remove it.
  5. 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.lock committed
  • [ ] CI runs npm audit --audit-level=high or equivalent and fails on CRITICAL/HIGH
  • [ ] No packages on latest or * 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)