Think Before You npm install

6 min read

The JavaScript ecosystem is amazing. A few keystrokes are the only thing standing between your project and myriad modules that will solve any number of problems. Most, if not all of us, have done the following in one of our JS projects:

  1. Run into a problem
  2. Find a node module through a quick Google search
  3. Run npm install for the module
  4. Write some code according to the module's docs
  5. Move on to the next problem

Often times, we add libraries to our projects to solve a problem without sufficiently evaluating the impact to our application. As I tell the teams I work with, once a module installed from npm is included in an app, you're accepting ownership of that code in your project.

Using npm modules is a great way to get things done. They allow us to take advantage of solutions built by and debugged by the larger community. We can reach for them to solve specific problems that are outside of our core domains. There are a lot of benefits of the node ecosystem, but you need to make sure you're making an informed decision that you can ultimately accept responsibility for within your code base.

Determine if you need it at all

The first thing you should ask yourself, is if you need an external library at all.

Can you avoid the problem entirely?

For particularly tricky problems, I like to consider if the problem can be avoided entirely. Sometimes, if you dig a little deeper, you can re-frame the questions that led you to the module you're about to install and it turns out, there is a completely different approach that removes the problem entirely.

This doesn't always work, but it's worth taking a step back and at least considering it.

Can you solve this locally?

Sometimes, you'll find that you can solve the problem yourself by building a focused utility. You shouldn't feel the need to build everything yourself, but if you have a small enough or specific enough need, sometimes just writing some tests and code is enough.

Do you already have a module implicitly installed?

You might also find that through sub-dependencies, you already have a module installed that serves your needs. Rather than installing a second solution to the same problem, you can just elevate that sub dependency to an explicit dependency of your project and use the module that is already getting bundled up with your code anyway.

Explore your options

If you've found a library that you think will meet your needs, keep looking. You should find a couple alternatives and then evaluate them all to determine the best choice for your project.

Inform your decision

Once you've determined that an external dependency is the right call and you've found a few options to solve the problem at hand, now it's time to get down to the real work of evaluating each option.

You should consider each of the following questions for each library you're considering.

How will it impact your production bundle?

The overall size of your JavaScript bundle should be kept in check. The more JavaScript the browser needs to parse, evaluate and execute, the slower your app will run. If you're working within a performance budget, this step can help you avoid breaking your budget. Even if you're using techniques like code-splitting and inlining critical CSS, a smaller starting point is certainly not going to hurt performance.

Since you likely have dependencies already, it's worth exploring your current bundle to see if one of your options is already being included in your production code. If so, you can add that module as an explicit project dependency and move on with your life.

If your option isn't already a sub-dependency that you can take advantage of, it's time to investigate how much it could impact your bundle size. For this, I reach for Bundlephobia. All you need to do is enter your package name in the search field and you'll get some metrics around the bundle size, both minified and minified & gzipped, along with approximate download times based on different network speeds.

Bundlephobia will also let you know if a library is tree-shakable, meaning dead code elimination will result in unused portions of the code being omitted from your production bundle.

Does it do too much, or too little?

Is this really the right tool for the job? Does it try to solve 100 problems when you only have one? If you'll only use a small subset of the library's available API, you should make sure it's tree-shakable so you don't end up shipping a lot of unused code in your production bundle.

On the flip-side of this, the API could fall short of what you need. If you end up having to write a lot of extra code to work around the library's short-comings, you might end up introducing more friction than you would with a more complete library or by writing all of the code in the first place.

How well maintained is it?

  • Are there recent commits?
  • Is the issue count reasonably low?
  • Are issues actively being discussed and closed out in a reasonable amount of time?
  • Are maintainers responsive to external contributors?

How good are the docs?

  • Is the README clear?
  • Are there external docs?
  • Is there a discord where this is actively discussed?
  • Can you find answers to your questions?
  • Is there information about different use cases, edge cases?

How well established is it?

  • How many downloads does it get?
  • Are you aware of other teams and companies who make use of it?
  • Is it used by projects you're aware of?

How clean is the source code?

If you had to take over this code base to add features and fix bugs, would you feel comfortable working with it?

Make an informed decision

With all the information you've gathered from the above steps, now you can choose an option, knowing that you know what you're getting into from a long-term ownership perspective.

Not all the boxes need to be checked. Certainly, a simple utility shouldn't be removed from contention because it doesn't have a thriving community in discord, it is a simple utility after all. These are just possible data points. Your needs and the overall context of the problem will determine how important each specific data point is in making this particular decision.

Clearly Communicate Your Decision

Once you've chosen a direction, make sure you document the decision and share the rationale behind it. All the careful consideration and research will help answer that question for other devs who work in the code now and in the future. Documenting the decision will also save a few steps and provide a jumping off point for future decisions.