This is a universal guide for auditing and managing dependencies in any Node.js/TypeScript project with a pragmatic, minimalist philosophy. Use this to audit any project and create a comprehensive DEPENDENCIES.md file.
Supports: npm, pnpm, yarn, bun Works with: Node.js, TypeScript, JavaScript, monorepos
To audit a project using this guide:
Using the DEPENDENCY_AUDIT_GUIDE.md as your reference, audit this project's
dependencies and create a DEPENDENCIES.md file following the template provided.
Claude will:
- Run audit commands for your package manager
- Analyze each dependency
- Categorize them (π’ Excellent / π‘ Acceptable / π΄ Questionable)
- Create a project-specific DEPENDENCIES.md
- Identify removable dependencies
- Suggest version pinning strategies
This guide works with any package manager. Commands are provided for all major tools:
- npm - Default Node.js package manager
- pnpm - Fast, disk-space efficient
- yarn - Classic or Berry (v2+)
- bun - Fast all-in-one toolkit
Most commands follow this pattern: <package-manager> <command> <args>
"A little copying is better than a little dependency" - Go Proverb
Apply this mindset to JavaScript: Every dependency is code you don't control, a potential security vulnerability, technical debt, and a maintenance burden. Choose wisely.
Run these commands to understand the project (adjust for your package manager):
# List direct dependencies only
npm ls --depth=0 # npm
pnpm ls --depth=0 # pnpm
yarn list --depth=0 # yarn
bun pm ls # bun
# Check node_modules size
du -sh node_modules/
# Check for security vulnerabilities
npm audit # npm
pnpm audit # pnpm
yarn audit # yarn
bun pm audit # bun (if available)
# Find outdated packages
npm outdated # npm
pnpm outdated # pnpm
yarn outdated # yarn
bun outdated # bun (if available)
# Check TypeScript configuration (if TypeScript project)
cat tsconfig.json | grep -A 5 "compilerOptions"Record:
- Total direct dependencies (production + dev)
- node_modules size
- Security vulnerabilities (severity and count)
- TypeScript strict mode status
For each production dependency, gather:
# Check version (any package manager)
npm view <package> version
pnpm info <package> version
yarn info <package> version
bun pm view <package> version
# Check transitive dependencies
npm view <package> dependencies
pnpm info <package> dependencies
yarn info <package> dependencies
# Check why it's installed
npm ls <package>
pnpm why <package>
yarn why <package>
# Check package details (weekly downloads, publish date, etc.)
npm view <package>
pnpm info <package>
yarn info <package>
# Or use online tools
# - https://bundlephobia.com (bundle size)
# - https://npmtrends.com (popularity trends)
# - https://www.npmjs.com/package/<package> (full details)Use this decision tree for each dependency:
- Zero transitive dependencies
- Small size (<100KB)
- Single, clear purpose
- Would take >50 lines to reimplement
- Examples:
- Validation: zod, yup
- HTTP: hono, fastify (lightweight frameworks)
- Database: postgres.js, better-sqlite3
- Utilities: date-fns (if you need it), ms, dotenv
- Few transitive deps (<10)
- Official SDK or well-maintained library
- Core to product functionality
- Examples:
- ORMs: drizzle-orm, prisma (if needed)
- Official SDKs: @google-cloud/, @aws-sdk/, stripe
- Frontend: React, Vue, Svelte (framework dependencies)
- Testing: vitest, jest (dev deps)
- Build: vite, esbuild (dev deps)
- Many transitive deps (>15)
- Large size (>500KB)
- Could be replaced with simpler approach
- Brings in duplicate dependencies
- Examples:
- Heavy cloud SDKs (consider REST API instead)
- All-in-one frameworks (evaluate what you actually use)
- Deprecated packages (lodash β native methods)
- Utility libraries for trivial tasks (left-pad, is-number)
Before keeping any dependency, ask:
-
Can I write this in <50 lines of code?
- If YES β Write it yourself
- If NO β Continue to question 2
-
Does it have minimal dependencies itself?
- Run:
npm view <package> dependencies(or pnpm/yarn equivalent) - RED FLAG: >10 transitive dependencies
- GREEN FLAG: 0-3 transitive dependencies
- Run:
-
Is it widely used and actively maintained?
- Check: npm downloads/week (prefer >100k)
- Check: Last update (prefer <6 months ago)
- Check: GitHub stars and open issues
-
What's the bundle size impact?
- Check: https://bundlephobia.com
- RED FLAG: >10MB for a utility library
- Consider: Is the size justified by value?
-
Could I use a REST API instead?
- Many SDKs are just wrappers around HTTP APIs
- Sometimes
fetch+ 100 lines of code beats a 50-dependency SDK
Look for:
Duplicate functionality:
# Check for multiple env loaders, loggers, http clients, etc.
npm ls | grep -i "dotenv"
npm ls | grep -i "logger"
npm ls | grep -i "http"
# (or use pnpm/yarn ls)Unused dependencies:
# Search for imports across codebase
grep -r "from ['\"]<package-name>['\"]" . --include="*.ts" --include="*.tsx" --include="*.js"
# Or use specialized tools
npx depcheck # Find unused dependencies
npx npm-check # Interactive dependency checkerRedundant type packages:
# Check if types are already provided by peer dependencies
npm ls @types/<package>
# (or use pnpm why / yarn why)Leftover dev dependencies:
- Check package.json for tools you're not using
- Look for packages from old configs or experiments
Categorize dependencies into pinning strategies:
When:
- Pre-1.0 packages (0.x.x versions) - minor bumps can break
- Database layers (ORMs, query builders) - critical stability
- Core business logic packages - breaking changes are catastrophic
- Packages with history of breaking changes
Format: "package": "1.2.3" (no ^)
When:
- Mature, stable packages (v3+, good semver history)
- Official SDKs from trusted vendors
- Well-maintained frameworks with good semver discipline
- Dev dependencies (always want latest tooling)
Format: "package": "^1.2.3" (allows 1.x.x updates)
Upgrade process for pinned packages:
- Check changelog for breaking changes
- Test in development environment
- Run full test suite
- Update manually:
pnpm add <package>@<version> - Document the upgrade in DEPENDENCIES.md
# Run security audit
npm audit # npm
pnpm audit # pnpm
yarn audit # yarn
# Check specific severity levels (npm/pnpm)
npm audit --audit-level=moderate
npm audit --audit-level=high
npm audit --audit-level=critical
# Fix automatically (use with caution)
npm audit fix # npm
pnpm audit --fix # pnpm (if available)
yarn audit --fix # yarn (deprecated, use upgrade-interactive)For each vulnerability:
- Identify if it's in production or dev dependencies
- Check if it affects your usage (many vulns are in unused code paths)
- Evaluate severity vs. effort to fix
- Document accepted risks (with justification)
Acceptable vulnerabilities:
- Low/Moderate in dev-only dependencies
- Vulnerabilities in unused code paths (document why it's unused)
- Awaiting upstream fixes (set reminder to check monthly)
Unacceptable vulnerabilities:
- High/Critical in production dependencies
- Known exploits in your attack surface
- No upstream fix available (consider alternatives)
Use this template structure:
# Dependency Management Guidelines
## Current State (Baseline)
- **Direct dependencies:** X (Y production, Z dev)
- **node_modules size:** XMB
- **Last audit:** YYYY-MM-DD
- **Security vulnerabilities:** X (severity breakdown)
- **Last cleanup:** YYYY-MM-DD
## Philosophy
> "A little copying is better than a little dependency" - Go Proverb
[Your project-specific philosophy paragraph]
## Before Adding a Dependency
Ask these questions in order:
1. **Can I write this in <50 lines of code?**
- If yes: Write it yourself
- Examples: String utilities, simple validators, array helpers
2. **Does it have minimal dependencies itself?**
- Run: `npm view <package> dependencies` (or pnpm/yarn equivalent)
- Red flag: >10 dependencies
- Check: When was it last updated?
3. **Is it widely used and trusted?**
- Check npm downloads/week
- Check GitHub stars and issues
- Prefer: >100k weekly downloads OR official SDK
4. **What's the bundle size impact?**
- Check: https://bundlephobia.com
- Red flag: >10MB for a utility library
## Version Management
### Pinned Dependencies (Exact Versions)
**These are pinned to prevent breaking changes:**
```json
// Pre-1.0 packages (0.x.x can break on minor updates)
"package-name": "0.20.2",
// Database layer (critical stability)
"orm-package": "1.44.6",
// Core business logic
"core-package": "5.0.76"
```
**To upgrade pinned dependencies:**
1. Check changelog for breaking changes
2. Test in development thoroughly
3. Update manually:
- `npm install <package>@<version>` (npm)
- `pnpm add <package>@<version>` (pnpm)
- `yarn add <package>@<version>` (yarn)
- `bun add <package>@<version>` (bun)
4. Update this document with new version
### Flexible Dependencies (Caret Ranges)
**These can auto-update within their major version:**
```json
// Mature, stable frameworks
"framework": "^4.10.1",
// Official SDKs (good semver discipline)
"official-sdk": "^7.17.2"
```
### Dev Dependencies
**All dev dependencies use caret ranges** - always get latest tooling improvements.
### When to upgrade
- **Pinned packages:** Manual upgrade only, after testing
- **Flexible packages (patch):** Auto-apply via update command
- **Flexible packages (minor):** Review changelog, test locally
- **Major versions:** Read migration guide, block time for testing
**Update commands:**
```bash
npm update # npm
pnpm update # pnpm
yarn upgrade # yarn
bun update # bun
```
## Security Protocol
### Monthly (Set calendar reminder)
```bash
npm audit && npm outdated # npm
pnpm audit && pnpm outdated # pnpm
yarn audit && yarn outdated # yarn
```
### Before deploying to production
```bash
npm audit --audit-level=high # npm
pnpm audit --audit-level=high # pnpm
yarn audit # yarn
# Fix any high/critical vulnerabilities before deploy
```
### Lockfile discipline
- **NEVER delete** lockfiles to "fix" issues
- `package-lock.json` (npm)
- `pnpm-lock.yaml` (pnpm)
- `yarn.lock` (yarn)
- `bun.lockb` (bun)
- **ALWAYS commit** lockfile changes
- **PRODUCTION:** Use exact versions in lockfile (default behavior for all package managers)
## Dependency Assessment
### Production Dependencies
| Package | Version | Transitive Deps | Size | Verdict | Rationale |
|---------|---------|-----------------|------|---------|-----------|
| package-name | 1.2.3 | 0 | 50KB | π’ Keep | Zero deps, minimal, essential |
| heavy-sdk | 5.0.0 | 15 | 500KB | π΄ Watch | Consider REST API alternative |
### Dev Dependencies
| Package | Version | Purpose | Verdict |
|---------|---------|---------|---------|
| typescript | 5.9.3 | Type checking | β
Essential |
## Current Vulnerabilities
### [Package Name] v[version] ([severity])
- **Status:** Accepted/Mitigating/Fixing
- **Path:** package β dependency β vulnerable-package
- **Risk:** [Description of actual risk]
- **Mitigation:** [Why it's acceptable or what you're doing]
- **Resolution:** [Waiting for upstream/Will fix in next release/etc]
## Approved Dependency Categories
### β
Always Acceptable
- Official SDKs (Google Cloud, OpenAI, etc.)
- Core framework dependencies
- TypeScript types (`@types/*`)
- Build/dev tools
### β οΈ Requires Justification
- Utility libraries (can you write it?)
- Additional ORMs/frameworks
- Alternative HTTP libraries
### β Generally Avoid
- Packages with <1000 weekly downloads
- Packages not updated in >2 years
- Micro-packages for trivial tasks (left-pad syndrome)
- Multiple packages doing the same thing
## Red Flags to Watch For
If you see these, investigate immediately:
- Audit shows HIGH or CRITICAL vulnerabilities
- node_modules grows beyond reasonable size (set your threshold: 500MB, 1GB, etc.)
- Direct dependencies exceed reasonable count (set your threshold: 25, 30, 50, etc.)
- Build time increases significantly
- New deprecation warnings appear
## Monitoring Commands
### See direct dependencies only
```bash
npm ls --depth=0
pnpm ls --depth=0
yarn list --depth=0
```
### Check node_modules size
```bash
du -sh node_modules/ # Unix/Mac
dir node_modules # Windows (shows size in properties)
```
### Why is this package installed?
```bash
npm ls <package-name>
pnpm why <package-name>
yarn why <package-name>
```
### Find outdated packages
```bash
npm outdated
pnpm outdated
yarn outdated
```
### Check package details
```bash
npm view <package>
npm view <package> dependencies
pnpm info <package>
pnpm info <package> dependencies
yarn info <package>
```
## Adding a New Dependency - Checklist
Before running install command:
- [ ] Checked if I can write it myself in <50 lines
- [ ] Reviewed package's own dependencies
- [ ] Verified last update was <6 months ago
- [ ] Checked weekly downloads (>10k preferred)
- [ ] Measured bundle size impact (bundlephobia.com)
- [ ] Read recent issues on GitHub
- [ ] Documented reason in git commit message
- [ ] Updated this DEPENDENCIES.md if it's a new category
## Update History
### YYYY-MM-DD: [Update Description]
**Updated:**
- `package`: X.X.X β Y.Y.Y
- **Compatibility:** β
/β οΈ/β
- **Breaking changes:** [List or "None detected"]
- **Notes:** [Any important details]
### YYYY-MM-DD: Initial Audit
**Removed X unnecessary dependencies:**
- `package-name` - Reason
- `another-package` - Reason
**Result:** XX β YY dependencies (-Z%)
## Resources
- Security advisories: `pnpm audit`
- Package info: `pnpm info <package>`
- Dependency tree: `pnpm ls <package>`
- Bundle analysis: https://bundlephobia.com
- Package trends: https://npmtrends.comAlways check that strict mode is enabled:
{
"compilerOptions": {
"strict": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
}
}This provides your safety net - it's 80% of the safety of Go/Rust's type systems.
Heavy SDKs that could be replaced:
- Cloud storage SDKs (often can use REST API with just
fetch) - Analytics SDKs (often can POST events directly)
- Social media SDKs (REST APIs are usually simple)
Frameworks that pull in the world:
- All-in-one frameworks (check what you actually use)
- State management libraries (could you use React Context?)
- Utility libraries (lodash, ramda - write the 3 functions you need)
Duplicate functionality:
- Multiple HTTP clients (axios, got, node-fetch, native fetch)
- Multiple date libraries (date-fns, moment, dayjs, native Date)
- Multiple validation libraries (yup, joi, zod - pick one)
- Multiple test frameworks (jest, vitest, mocha - pick one)
Initial project setup:
- Run the audit process
- Create DEPENDENCIES.md
- Establish baselines and thresholds
Monthly maintenance:
- Run audit and outdated commands for your package manager
- Review any new dependencies added
- Update DEPENDENCIES.md with changes
Before adding new dependencies:
- Consult the "Before Adding a Dependency" checklist
- Document the decision in DEPENDENCIES.md
- Update the assessment table
When onboarding new developers:
- Have them read DEPENDENCIES.md
- Explain the project's dependency philosophy
- Use it as a code review checkpoint
Monorepos:
- Run audit at root level and in each workspace
- Watch for duplicate dependencies across workspaces
- Use workspace protocol to share dependencies:
"@my/package": "workspace:*" - Consider tools like
manypkg checkorsyncpackfor consistency
Frontend + Backend:
- Maintain separate DEPENDENCIES.md for each if significantly different
- Or use sections: "Frontend Dependencies" and "Backend Dependencies"
- Watch for unnecessary shared dependencies
A well-managed Node.js project should aim for these targets (adjust based on project type):
- β <30 direct dependencies (total)
- β <500MB node_modules
- β 0 high/critical security vulnerabilities
- β >50% dependencies with 0 transitive deps
- β <50 direct dependencies (frameworks bring more deps)
- β <1GB node_modules (build tools are larger)
- β 0 high/critical security vulnerabilities
- β >30% dependencies with 0 transitive deps
- β TypeScript strict mode enabled (if using TypeScript)
- β All dependencies documented and justified
- β Clear pinning strategy
- β Monthly audit cadence
- β No duplicate functionality
- β Lockfile committed to version control
When evaluating a JavaScript dependency, ask:
"If I were writing this in Go, would I import a library for this?"
If NO β You probably don't need the JavaScript library either.
Examples:
- String manipulation β Use native methods
- Simple HTTP requests β Use fetch
- Date formatting β Use Intl.DateTimeFormat
- Array utilities β Use native map/filter/reduce
- Retry logic β Write a 10-line retry function
If YES β The dependency might be justified.
Examples:
- Database drivers (you'd use
database/sql+ driver) - HTTP routing (you'd use
net/httpor chi/gin) - Validation (you'd use a validator package)
- ORM functionality (you might use sqlc or gorm)
This dependency philosophy is about conscious choices:
- Every dependency should earn its place
- Prefer small, focused packages over frameworks
- Value zero-dependency packages highly
- When in doubt, write it yourself
- Security and maintainability over convenience
Remember: Dependencies are liabilities. Treat them like technical debt - some is necessary and good, but too much will sink the project.