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.
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.
Move packages to the correct section using the explicit save flags:
# 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.
ID: dependency-supply-chain.optimization.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:
# 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.