How to maintain npm dependencies in your project
It’s very common that we find projects working correctly in production but are no longer actively maintained—it’s in production, it works, and the client considers the project finished.
Unfortunately, this is not entirely true. We tend to forget that when a project is finished and it’s in production, it does not mean that it doesn’t need any maintenance. Most projects still need to be revised and incidents need to be resolved when they occur. If we compared it to buying a vehicle, it would be something like this: we go to the dealer, we pick the right model, we pay, we enjoy it for as long as it’s useful without doing any periodic maintenance, right? Of course not—and the same holds for software.
When developing REST API projects or CLI Applications with Node.js, it is very common to use the open source npm dependency package manager to include frameworks and tools in these projects.
To a certain extent, this is beneficial as it can help us reduce development time. Including stable packages that are well tested and maintained by the community, serves certain needs for our project. However, it can also turn against us and be a problem.
Below, we discuss a common scenario with a Node.js project. Some of these problems occur when we leave a project unattended and without evolutionary and preventive maintenance.
Imagine a REST API project with Node.js that has been in production for 2 years. or more. During those two years, no one has been taking any steps to update or adapt dependencies to new versions. For its development, the express package was used as a base framework for CRUD functionality. In addition, the
jsonwebtoken library was used to generate security tokens and the moment package for the internationalization of dates.
The use of fairly new dependency libraries can be considered the first issue, as we base part of our solution on software not tested by the community and which, possibly, doesn’t have a constant evolution.
Secondly, basing part of our solution on “mono-contributor” libraries, as I call them. These dependencies are developed by a single person who is the one releasing the improvements, maintaining, and resolving incidents and, finally, unilaterally determining the path that this software should go. Those dependencies limit us significantly.
As a third problem, we have the dependencies that contain other dependencies. However, this is not visible at first glance, what we commonly call “Node Module Hole”.
See more tips about it here.
1. Do I really need this dependency?
In a Node.js project, the first place we should look for information is in its own documentation. In the Node.js API documentation, there is a large number of practical examples of what we can achieve for ourselves by working “natively” with the Node.js API, without external dependencies.
2. Use of micro-dependencies
Once we are clear that we need a unit to cover the needs of our project, we have to find the right one. At first, we often think that the first option that appears in the result of our Google search (or in the npm search engine) is going to be the most appropriate, but in many cases, it is not. We must stop, think, and analyze how much of what this library does we actually need. Perhaps it does much more than we need, dragging into our project a library that will largely be unused.
3. Market analysis
We must do a little fieldwork and look for alternatives—the npm ecosystem is very large and we can find different libraries that practically do the same. But which one to choose? In this case, we can use web tools such as Bundlephobia that will show us a comparison of the size of each package, or npmtrends that shows us a comparison with information on download volume or community contributions, among other parameters.
These tools can help with the selection of the right dependency. Information like the size of the dependency, the volume of the community that maintains it, the number of releases it has had in recent months, or the number of downloads, can be important indications to consider.
4. Use of preventive tools
Snyk has released the open source extension Vuln Cost for Visual Studio Code that allows us to visualize in a very easy and fast way how many vulnerabilities our software has from the file
package.json, and even from each file that contains a library import.
Currently, in a Node.js project we can inject dependencies through
import, adding them as a whole or partially, if allowed. In the following image you can see a clear example of how this looks using the previously described project (express + jsonwebtoken + moment):
If you are looking for more resources on how to use Vuln Cost, check out the three videos below:
- How to install Vuln Cost extension in Visual Studio Code
One of the challenges that the software industry has always faced is explaining and making the end-user understand that software has a basic need: to be maintained and evolved, seeking, for example, adaptation to operating system updates, to improve its performance, or eliminate possible vulnerable points.
Another challenge is to get time for development teams to think, update, and train in order to find the most suitable alternatives when developing software—the first choice that comes across our table isn’t always the right one.
The four solutions we explored above can help you develop a maintainable, high-quality, scalable, and more secure codebase.