This blog was written by Benjamin Coe. Ben works on the open-source libraries yargs, nyc, and c8, and is a core collaborator on Node.js.
Maintainers Should Consider Following Node.js’ Release Schedule
This blog was written by Benjamin Coe. Ben works on the open-source libraries yargs, nyc, and c8, and is a core collaborator on Node.js. He works on the client libraries team at Google. This piece originally appeared on the Node.js Collection. Node.js is an impact project of the OpenJS Foundation.
tldr; Node.js has a tried and true release schedule, supporting LTS versions for 30 months. It offers significant benefits to the community for library maintainers to follow this same schedule:
My opinion of what Node.js versions library maintainers should aim to support has evolved over the years. Let me explain why…
I joined npm, Inc in April 2014. During this period, releases of Node.js had stalled. Node.js v0.10.x was released in April 2013, and Node.js v0.12.x wouldn’t be released until February 2015.
At the same time, the npm package registry was going through growing pains (see: “Outage Postmortem”, “Four hours of partial outage”, etc.).
The state of Node.js and npm in 2014 had side effects on how folks thought about writing libraries: maintainers didn’t need to put mental overhead into deciding what Node.js versions they supported (for years, the answer was 0.10.x); partially owing to npm’s instability, and partially owing to frontend communities not having fully embraced npm for distribution, package dependency trees were smaller.
Small building blocks, like mkdirp, still represented a significant portion of the registry in 2014.
Things would change in the intervening six years…
In February of 2015, motivated by the io.js fork, The Node.js Foundation was announced. In September of that same year, Node.js v4.0.0 was released. Node.js v4.0.0 merged the io.js and Node.js projects unblocked the release logjam and introduced the 30-month LTS cycle advocated in this article.
Since Node.js v4.0.0, maintainers have been able to count on a regular cadence of releases, pulling in new JavaScript language features (through V8 updates), additions to the standard library (like HTTP/2), and important bug and security fixes.
In parallel, during the period between 2014 and today, npm significantly improved stability, and the frontend community began to consolidate on npm for distribution. The side effect was that many more packages were being published to npm (numbers grew from 50,000 in 2014, to 700,000 in 2018). At the same time, dependency trees grew (the average number of dependencies in 2016 was 35.3, the average number of dependencies in 2018 was 86).
A library maintainer in 2020 has at least three versions of Node.js to think about (the Current, Active, and Maintenance versions). Also, on average, their libraries rely on an increasing number of dependencies… it can be a bit daunting!
A library maintainer in 2020 has at least three versions of Node.js to think about (the Current, Active, and Maintenance versions).
A great way for maintainers to handle the increasing complexity of the JavaScript ecosystem is to consider adopting Node.js’ 30-month LTS schedule for their own libraries.
Here’s how adopting this schedule benefits both the library authors and the community…
A security vulnerability was recently reported for the library minimist. minimist is the direct dependency of 14,000 libraries… it’s a transitive dependency of the universe.
The popular templating library Handlebars was bitten by this report through an indirect dependency (optimist). Handlebars was put in a position where it was difficult to silence this security warning without disrupting its users:
Although motivated by good intentions (“why not support as many environments as possible?”), when libraries support end-of-life versions of Node.js, it can ultimately end in disruptions for users. Maintainers find themselves bumping a major version as a fire drill, rather than as a scheduled update.
Dropping support for old @nodejs release is a breaking change and it should be released in a major version.. — Matteo Collina
The wide adoption of Node.js’ LTS schedule for modules ensures that security patches can always be taken.
Keeping dependencies up to date is a lot of work (my team at Google landed 1483 pull requests updating dependencies last month), it’s also important:
Tools like Dependabot and Renovate make sure updating dependencies isn’t a maintainer’s full-time job. However, if libraries don’t adhere to the same version support policy, it makes automation difficult. As an example, because of falling behind the scheduled deprecation of Node.js v8.x.x, the library yargs turned off automatic updates for decamelize (opening itself up to all the risks that go along with this).
A lot of open-source is made possible by the volunteer work of maintainers. I can’t think of many things less exciting than the constant auditing of the SemVer ranges advertised in the “engines” fields of dependencies.
The wide adoption of Node.js’ LTS schedule for modules creates consistency and reduces the maintainer burden around updating dependencies.
For the last couple of years, I’ve been involved in the Node.js Tooling Group. We’ve advocated for a variety of API improvements for tooling authors, such as recursive directory creation, recursive directory removal, and Source Map support for stack traces.
In Node.js v8.4.0, http2 support was added. This addition is near and dear to my heart since it allows Google’s client libraries (which rely on HTTP/2) to run natively on Node.js.
JavaScript itself is an evolving platform. Node.js regularly updates the V8 JavaScript engine, pulling in new language features, such as async iterators, async/await, and spread operators.
Keeping the Node.js core small will always be an architectural goal of the project. Node.js is, however, an evolving platform.
The wide adoption of Node.js’ LTS release schedule allows module authors to leverage exciting new features that are added to the platform.
What actions am I advocating that library maintainers take?
The Node.js Package Maintenance Working Group is developing further recommendations related to library support policies, such as Cloud Native’s Long Term Support for Node.js Modules. This policy takes this article’s recommendations a step further and suggests that module maintainers support a major library version for the lifetime of the Node.js runtime it was released under. This means that, if your library releases v1.0.0 with support for Node v10.x.x, you would continue to backport security and bug fixes to v1.0.0 until Node v10.x.x reaches end-of-life. Committing to this level of support can be a great move for libraries targeting enterprise users (and, they even have a badge!).
Tracking Node.js’ release schedule saves you time, makes libraries more secure, allows you to use Node.js’ fancy new features, and benefits the community as a whole. Let’s get good about doing this together.