DEV Community

Atlas Whoff
Atlas Whoff

Posted on

npm Dependency Hygiene: When to Install vs. Write It Yourself, and Security Auditing

The Real Cost of npm install

Every dependency you add is:

  • Bundle size (slows your app)
  • Attack surface (security vulnerabilities)
  • Maintenance burden (breaking changes, abandoned packages)
  • Transitive dependencies (packages of packages you didn't choose)

Here's how to evaluate whether a dependency is worth adding.

The Five-Question Framework

Before npm install [package]:

1. Can I write this in < 20 lines?
   If yes: just write it. No dependency needed.

2. How many weekly downloads does it have?
   < 10,000/week: potentially unmaintained, check carefully
   > 1M/week: widely used, likely well-maintained

3. When was the last commit?
   > 2 years: probably abandoned. Look for alternatives.

4. How many open CVEs does it have?
   npm audit will tell you. Zero is the target.

5. How many transitive dependencies does it add?
   npm install --dry-run shows the count.
   A util adding 50 transitive deps is a red flag.
Enter fullscreen mode Exit fullscreen mode

Things to Write Instead of Install

// Don't install: lodash (for one function)
// Write:
function chunk<T>(arr: T[], size: number): T[][] {
  return Array.from({ length: Math.ceil(arr.length / size) },
    (_, i) => arr.slice(i * size, i * size + size))
}

// Don't install: uuid (for random IDs)
// Use built-in:
const id = crypto.randomUUID()

// Don't install: date-fns (for basic formatting)
// Use built-in:
new Date().toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' })

// Don't install: is-url (for URL validation)
// Use built-in:
function isUrl(str: string): boolean {
  try { new URL(str); return true } catch { return false }
}

// Don't install: left-pad (learned this lesson in 2016)
// Use built-in:
'abc'.padStart(10, '0') // '0000000abc'
Enter fullscreen mode Exit fullscreen mode

Security Audit Workflow

# Before shipping:
npm audit

# Fix automatically:
npm audit fix

# Check what changed:
npm audit fix --dry-run

# Force fix (may break things, verify):
npm audit fix --force

# In CI -- fail if critical vulnerabilities:
npm audit --audit-level=critical
Enter fullscreen mode Exit fullscreen mode

Keeping Dependencies Updated

# See outdated packages
npm outdated

# Interactive update tool
npx npm-check-updates -i

# Update all minor/patch:
npx npm-check-updates -u --target minor
npm install
npm test  # Verify nothing broke

# Dependabot (GitHub): auto-PRs for security updates
# .github/dependabot.yml
Enter fullscreen mode Exit fullscreen mode
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: npm
    directory: /
    schedule:
      interval: weekly
    ignore:
      - dependency-name: '*'
        update-types: ['version-update:semver-major']
Enter fullscreen mode Exit fullscreen mode

Tree Shaking: Only Ship What You Use

// WRONG -- imports entire library
import _ from 'lodash'
const result = _.groupBy(items, 'category')

// RIGHT -- imports only what's needed
import groupBy from 'lodash/groupBy'
const result = groupBy(items, 'category')

// EVEN BETTER -- use native (no import)
const result = Object.groupBy(items, item => item.category)
// (Node 21+, modern browsers)
Enter fullscreen mode Exit fullscreen mode

MCP Server Dependencies

MCP servers have the same dependency risks as any Node.js package -- plus they run inside your AI session.
A compromised transitive dependency in an MCP server can access everything the server can access.

The MCP Security Scanner audits MCP server dependencies as part of its security check.

$29/mo at whoffagents.com

Top comments (0)