In this article, I want to elaborate on things that belong to the
dependencies section in package.json and the ones that should be in
What do you mean by "incorrectly"?
When I ask a person why they installed Webpack with
--save-dev, the most common response is "
--save is only for what makes it into runtime while
--save-dev is for development and build". However, they can't prove their point with anything other than the esthetic and logical distribution of packages. So, no practical advantages. The worst part about it is that major vendors also suggest installing their packages with that option. For example, different Webpack loaders:
What's the "correct" way?
devDependencies in package.json, there's also an interesting
--production option, made specifically for these sections.
npm install --production
npm ci --production
Both of these commands install dependencies in the project but only from the
dependencies section. Come to think of it, is there any sense in that if we can't do anything with packages from the
dependencies section without the ones from
devDependencies? Indeed, if we think about it in this way, there's no point in that flag.
In reality, there is, but under the condition of right use of
--save-dev. If a package is necessary for the production build, install it with
--save. Otherwise use
--save-dev. For example, we can't build for production without Webpack and Webpack CSS Loader, therefore they must be installed with
--save. At the same time, Webpack Dev Server, Jest, or ESLint are not mandatory for the build, hence
The profit is,
npm install --production and
npm ci --production will not download spare dependencies, which aren't going to be used in the pipeline, during the building stage in CI . The benefits are obvious: it simply saves you build time, especially if you've got some bloated libraries in dev-dependencies that download (or even compile) binary files via
Important to realise!
💡 The common misconception here is that you may think
dependencies is for the project to work while in reality it's for the project to start working. No matter what makes it into runtime: package.json is far from being about that.
For instance, a doc article at npmjs.org explains the meaning of this field in a very straight-forward way:
"devDependencies": Packages that are only needed for local development and testing.
If we take a look at a neighboring tool, Composer (package manager for PHP inspired by npm) also recommends to put packages for testing and alike into
Lists packages required for developing this package, or running tests, etc. The dev requirements of the root package are installed by default. Both
--no-devoption that prevents dev dependencies from being installed.
So does Rust's Cargo with its
Sometimes there is a need to have dependencies for tests (examples, benchmarks) only. Such dependencies are added to
Are there any reasons to do it differently?
Why yes, you could find reasons to do things in a different way. This article's purpose is exactly for you to think about where you list your dependencies. Based on feedback, I noted two cases when it's possible to ignore these rules:
- You are working on a project that is going to be installed as a dependency, e.g. a library. In this case, you should keep in mind how everything works and refrain from filling
dependencieswith things that are not actually needed for the project to work. For example, you could have developed a React component that uses Lodash under the hood and publish it in the npm registry - in that case, React itself could be in
peerDependencieswith Lodash in
devDependencies. Otherwise the user would download both React and Lodash along with your component and you know how that would end up. On another hand, libraries that were written specifically for Node.js may not have been compiled in advance and are published as-is in most cases, therefore if your library depends on something like fs-extra, it's safe to put that in
dependencies, nothing bad will happen.
- The second special case is projects where frontend, backend, and SSR are all one. There, you have to build the bundle and leave node_modules for Node.js to work. Everything is not so trivial in this case and, as I think, it's up to personal preferences: no silver bullet here.
In case you are developing frontend with no backend, just backend, or CLI that will then be wrapped in pkg, then this article's principles will work perfectly.
The content of this text is food for thought on how to manage dependencies in npm. If you don't trust the author or straight-up don't understand what they're trying to say, try thinking about npm's
--production flag and package.json's
devDependencies on your own to come up with a conclusion.
ℹ️ The author rushed the article with a bold headline and forgot to cover many edgecases which cost them a bunch of comments. The text was extended several times in response to that. Thank you!