What is the difference between `dependencies` and `devDependencies` in `package.json`? Why does the distinction matter?
Show answer — try answering out loud first
dependencies are the packages your application needs to run in production (for example express): without them the program won't run. devDependencies are the packages you only use during development (for example vitest, nodemon, eslint): they help you test, reload, or check the code, but they aren't needed once the app is running on the server. They're installed differently (npm install X vs npm install -D X), and separating them well keeps the deploy lighter and safer.
The mental rule is simple: "does my app break if this package is missing in production?"
- If the answer is yes, it breaks -> it goes in
dependencies. Examples:express(the server framework), a database client, a validation library you use at runtime. - If the answer is no, I don't need it in production -> it goes in
devDependencies. Examples:nodemon(restarts the server on save, only on your machine),vitestorjest(run the tests),eslintandprettier(check and format the code).
How they're installed:
npm install express-> adds it todependencies.npm install -D nodemon(or--save-dev) -> adds it todevDependencies.
Why the distinction matters:
- Deploy size: in production you install with
npm install --omit=dev(or by settingNODE_ENV=production), which skips alldevDependencies. That way the server doesn't download things likeeslintorvitest, which can add up to hundreds of megabytes. Less weight means faster builds and Docker images. - Smaller attack surface: every installed package is third-party code that could have vulnerabilities. If you don't install development tools in production, you reduce the risk.
- Clarity: anyone reading the
package.jsonimmediately understands what's essential to run the app and what's just for the working environment.
Key point: both are installed when you run a plain npm install (on your development machine). The difference shows up when you deliberately omit the development ones on the server.
Installing each type of dependency from the terminal:
# PRODUCTION dependency: the app needs it to work
npm install express
# explicit equivalent:
npm install --save express
# DEVELOPMENT dependencies: only for working on the project
npm install -D nodemon vitest eslint
# explicit equivalent:
npm install --save-dev nodemon vitest eslint
# On the production SERVER, install only the essentials (omit devDependencies):
npm install --omit=dev
How it looks in the package.json:
{
"name": "mi-api",
"version": "1.0.0",
"dependencies": {
"express": "^4.19.0"
},
"devDependencies": {
"nodemon": "^3.1.0",
"vitest": "^1.6.0",
"eslint": "^9.0.0"
}
}
Here express is in dependencies because the server loads it with require('express') every time it handles requests. nodemon, vitest, and eslint are in devDependencies because they're only used while you develop: none of them run once the app is serving real traffic.
Putting something in dependencies that's actually a development tool (or the other way around). The most dangerous mistake is leaving a package that is used at runtime in devDependencies: on your machine everything works because npm install installs both groups, but when you deploy with --omit=dev that package isn't installed and the app crashes in production with a Cannot find module. Golden rule: if your app code does a require of that package while it's running, it goes in dependencies.
"dependencies are the packages the app needs to run in production, like express, while devDependencies are tools I only use while developing, like nodemon, vitest, or eslint. I install them differently: npm install for production and npm install dash D for development. The distinction matters mainly because of the deploy: on the server I install with --omit=dev to skip the development ones, and that way the image is lighter, faster to build, and has a smaller attack surface. The rule I use is: if my code does a require of that package at runtime, it goes in dependencies; if it's only for testing, reloading, or formatting, it goes in devDependencies."
You're building an API with Express, testing it with Jest, and reloading it in development with nodemon. You also use dotenv to read environment variables when starting the server. Where does each package go: dependencies or devDependencies?
See answer
express->dependencies(the server needs it to work).dotenv->dependencies(it runs at startup, in production too, to load the environment variables).jest->devDependencies(it only runs the tests, not needed in production).nodemon->devDependencies(it only reloads the server on your machine; in production you usenodedirectly).
The trap is dotenv: even though it sounds like a tool, it runs at runtime, so it goes in dependencies.