DEV Community

Dakota Lewallen
Dakota Lewallen

Posted on

2 Ways to Improve your Node.js Application

Use npm ci --production. Thanks for coming to my ted talk. 🎉

Kinda kidding, but not really.

For demonstration purposes, lets define a sample package.json, omitting some of the less important stuff.

{
  "name": "test project",
  "version": 1.0.0,
  "description": "Our sample project",
  "dependencies": {
    "first-dep": "1.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

So let's say you've been hard at work on this "test project" all morning working with the "first-dep" library. Unfortunately for you, "first-dep" released a minor version the night before, going up to "1.1.0" and breaking one of the API's you use. You run your test suite locally and everything is as green as grass. You push your code and egads batman a bunch of tests are broken. You run your tests locally over and over again racking your brain for what's happening. So you push another change just to see and watch the integration environment break again. So as is common practice in Node, you delete your local dependencies, reinstall and boom. Tests are broken. Finally. Now you go to source control provider to look for what's changed and you see a single change in your package.json

-    "first-dep": "1.0.0",
+    "first-dep": "1.1.0",
Enter fullscreen mode Exit fullscreen mode

The dreaded stealth patch. After some investigation, you discover that in your testing setup you run npm install. Causing the version to get modified in that environments package.json before it was changed in yours. Now you've come to a cross roads. Typically most updates your dependencies release don't directly affect your project and are fine to push through. But wait!

There is another

npm ci stops this discussion dead in its tracks. Using ci over install in your build environments defends against this versioning mismatch.

(npm) will never write to package.json or any of the package-locks: installs are essentially frozen. source

This brings us to the first usage improvement

  1. Create highly reproducible builds of your dependencies

As long as you haven't specified dependency changes, there shouldn't be any in your automated environments.

What does this mean?

Typically, when you run the command npm install, you will have your dependencies installed, as well as some of your dependency versions updated if there are updates available. This can lead to some hard to find issues when running in an automated environment, similar to the one above.

  1. Smaller production bundles

Going back to our example package.json. Lets say we want to add typescript to our workflow. First thing you'll want to do is npm install typescript. Now our package.json looks something like,

"dependencies": {
  "first-dep": "1.0.0",
  "typescript": "^4.4.3"
}
Enter fullscreen mode Exit fullscreen mode

But typescript isn't required to run our application. Typescript is a build tool. So even though we're only using it some of the time, it will be included in our node_modules every time. Irregardless of what command we use to set them up. To address this, we should pass the "-D" flag when installing packages that aren't required to run our application in production. So now our command should look like npm install -D typescript. Looking at our revised package.json, you should see something like,

"dependencies": {
  "first-dep": "1.0.0",
},
"devDependencies": {
  "typescript": "^4.4.3"
}
Enter fullscreen mode Exit fullscreen mode

Awesome! But... typescript still shows up in node_modules when you run npm ci. So it's great that we've started dividing our dependencies up between production and development, but the default behavior of both install commands is to install all dependencies. So we've got one more piece to this puzzle and that is the --production flag. This instructs npm that we want to ignore all of the packages listed in devDependencies.

So in short, if you're running your application in an automated environment (shared development servers, production, testing) make sure you setup your dependencies with the command npm ci --production. And while you're working any time you install a dependency that is not directly required by your app, install it with the "-D" flag.


Find me on Twitter | LinkedIn

Sponsor me on Github

Like the article? Buy me a coffee!

Discussion (0)