All Docs
FeaturesMaking Tax DigitalUpdated March 10, 2026

Blog: Keeping @types/* Out of Production — How We Fixed DEP-17

Keeping @types/* Out of Production — How We Fixed DEP-17

Release: v1.0.387
Category: Dependency hygiene / Build quality


What happened

During a routine dependency audit we identified that @types/qrcode was listed under dependencies in package.json rather than devDependencies. While this doesn't cause any visible runtime error, it is a meaningful miscategorisation with real consequences for production builds.

Why it matters

TypeScript @types/* packages are compile-time only

@types/* packages from DefinitelyTyped exist solely to give the TypeScript compiler the type information it needs to check your code at build time. Once your TypeScript source is compiled to JavaScript, those type declarations are no longer referenced — the output JavaScript has no knowledge of them.

Shipping @types/qrcode in production dependencies means:

  • Every production npm install downloads and installs a package that will never be called at runtime.
  • Production container images and deployment bundles are larger than they need to be.
  • Dependency count thresholds (like our internal DEP-11 check, which flags projects exceeding 30 production deps) are skewed, producing false positives that obscure genuinely necessary production packages.

The numbers

BeforeAfter
Production dependencies count3130
DEP-11 threshold status❌ Over limit (31 > 30)✅ Pass (30 = 30)
@types/qrcode installed in prodYes (wasted)No
TypeScript compilation✅ Unaffected✅ Unaffected
Runtime behaviour✅ Unaffected✅ Unaffected

The fix

The change is a single move in package.json — from the dependencies block to devDependencies:

// Before (incorrect)
{
  "dependencies": {
    "@types/qrcode": "^x.x.x"
  }
}

// After (correct)
{
  "devDependencies": {
    "@types/qrcode": "^x.x.x"
  }
}

To apply the same fix locally or in any downstream fork, run:

npm remove @types/qrcode
npm install --save-dev @types/qrcode

General rule of thumb

As a project convention, all @types/* packages must live in devDependencies unless there is an exceptional, documented reason otherwise (there essentially never is). Before merging any PR that adds a new type declaration package, verify the target block in package.json:

# Quick check — should return empty for production deps
npm ls --prod | grep '@types/'

If that command returns anything, a @types/* package has been placed in the wrong block.

No action required

This is a build-time housekeeping fix. No API behaviour, no user-facing features, and no runtime logic were changed in this release. Existing deployments do not need to be restarted.