# No dev dependencies serving production runtime roles

- **Pattern:** `ab-000968` (`dependency-supply-chain.optimization.no-dev-in-prod`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000968
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000968)

## Why it matters

A runtime package in `devDependencies` will silently disappear when your deployment runs `npm install --production` or `NODE_ENV=production` — causing runtime crashes that only surface in production, not local development or CI. Conversely, build tools like `jest`, `eslint`, and `typescript` in `dependencies` bloat your production install, increase container image sizes, and expand the attack surface of your production environment with tooling that was never meant to be deployed. OWASP A06 applies when the wrong set of packages is present in the production environment. CWE-1357 covers unnecessary components in the production software composition. SSDF PW.4 requires that the production component set be intentionally defined and reviewed.

## Severity rationale

High because a runtime package accidentally in devDependencies causes guaranteed production crashes when installed with `--production` or `NODE_ENV=production`, while build tools in dependencies silently expand production attack surface.

## Remediation

Move packages to the correct section using the explicit save flags:

```bash
# Move a runtime package from devDependencies to dependencies
npm install package-name --save-prod

# Move a build tool from dependencies to devDependencies
npm install package-name --save-dev
```

For Next.js and similar SSR frameworks: `dependencies` should include any package imported in server-side code, API routes, or middleware — not just client-side imports. The `--omit=dev` flag in production installs skips devDependencies entirely, so anything imported at runtime must be in `dependencies`.

## Detection

- **ID:** `no-dev-in-prod`
- **Severity:** `high`
- **What to look for:** Examine `devDependencies` for packages that should be in `dependencies` because they are required at runtime. Red flags: `dotenv` in devDependencies when `.env` loading happens in production code, server framework packages (`express`, `fastify`, `hono`) in devDependencies, ORM packages (`prisma`, `drizzle-orm`) in devDependencies when the project has a database, auth packages (`next-auth`, `@supabase/supabase-js`) in devDependencies. Conversely, check `dependencies` for packages that should be in devDependencies: `jest`, `vitest`, `eslint`, `typescript`, `@types/*`, `prettier`, test utilities. Having test frameworks in `dependencies` inflates production install size. Count all instances found and enumerate each.
- **Pass criteria:** Development-only dependencies must not appear in the production dependency list. Check that testing frameworks, linters, formatters, type checkers, and build-only tools are listed under "devDependencies" rather than "dependencies" in the manifest. Production bundles should not include dev tooling that adds install time, attack surface, and deployment weight without contributing to runtime functionality. At least 1 implementation must be confirmed.
- **Fail criteria:** A package required at production runtime is only in `devDependencies`, or test/build tooling is in `dependencies` (not just devDependencies).
- **Skip (N/A) when:** No `package.json` detected.
- **Detail on fail:** `"'dotenv' is in devDependencies but is used in production server initialization — move to dependencies"` or `"'jest' and 'vitest' are in dependencies — these are test frameworks and belong in devDependencies"`
- **Remediation:** Misplaced dependencies cause two distinct problems: runtime packages in devDependencies cause production crashes when `NODE_ENV=production` installs skip devDependencies. Build tools in dependencies bloat your production install.

  Move packages to the correct section:
  ```bash
  # Move a package from devDependencies to dependencies
  npm install package-name --save-prod

  # Move a package from dependencies to devDependencies
  npm install package-name --save-dev
  ```

  For Next.js, Nuxt, and similar frameworks that handle bundling: `dependencies` vs `devDependencies` distinction is about what gets installed in CI/production environments, not about the bundle. Use `dependencies` for any package imported in server-side or API code.

## External references

- cwe CWE-1357
- owasp A06
- ssdf PW.4

Taxons: supply-chain

HTML version: https://auditbuffet.com/patterns/ab-000968
