Why is Node 14/16 so hard to leave?
Node v14 and v16, while perhaps not as broadly or deeply relied on as something like Java 8, nonetheless have a bit of a reputation for being difficult to push past.
Starting in Node 15, the included npm used a new lockfile format, and starting in Node 17, support for insecure SSL options was dropped, which resulted in a number of breaking changes for developers.
It’s hardly news to suggest that the JS community has a slight tendency to emphasize rapid development, which is often paid for in deprecated and unsupported dependencies. Let it never be said that the good people of npm are unduly afraid of incrementing a major version 😄
Shortly after each of these releases, several common dependencies introduced breaking changes that were also difficult or impossible to integrate into projects that ran on v14 or older. This, combined with Node’s tight support cycles, makes it easy to end up with a stack of entirely deprecated software.
But before you embark on your journey of a thousand miles, consider this:
You may not need to upgrade yet
Far be it from me to suggest sticking with something deprecated, but a brief disclaimer is probably warranted. Depending on how you’re using Node, the effort to upgrade it might be wasted. Or at least misplaced.
If your main use-case for Node is to build out frontends that are deployed statically, you may not need to migrate! I’ve seen organizations burn significant effort migrating dozens of frontends to newer versions of essentially a build tool that never directly sees the light of prod.
Now, there are still good reasons to use newer build tools, but a Node deprecation may not be as dire as it first appears. If your candidate projects are going to be sticking around for a while, and would benefit from the newer features, absolutely give it the time! Just – measure twice before getting half your dev team to drop cycles that could be better spent elsewhere.
That said,
Maybe you do need the upgrade
It is possible that you’ll find yourself deploying a frontend built with dependencies that manage to leave your users vulnerable, regardless of how secure your side is. In the (admittedly, somewhat rare) case where you have such a dependency, and you can’t patch it without upgrading Node, then you best get to upgrading!
Now, if you have Node running live in production (e.g. an express API), your palette of risks takes on a different color, and the notion of getting your projects up-to-date should probably occupy a greater percentage of your thoughts.
Alright, I did the math: we do need to upgrade
There’s a good chance you’re in for a bumpy ride.
The OpenSSL changes in Node 17 are probably the biggest source of grief in trying to make this leap.
The issues are fairly widespread, and ugly to work around. Worse, which dependencies are broken
has gone largely untracked. There’s no clear way to retroactively indicate that a library is
incompatible with a new version of Node, so you end up with a lot of dependencies that allege
support for “node >= 10”, but in reality can’t run without ugly hacks after Node 16.
We’ve been using some heuristics, which I’ve tried to cram into a scripted process and encapsulate here, in the hopes it might prove useful for someone (possibly our future selves).
Essentially, we’ve
- Scanned for high-profile issues containing the relevant error code:
ERR_OSSL_EVP_UNSUPPORTED - Compiled a list of the repos where these types of issues were reported
- Checked against the npm registry if those repos are actually published packages
This resulted in a list of npm packages, sorted by recent downloads.
You can paste a dependencies object (or a simple comma-separated search) into the filter to check
for any matches.
send
loader-utils
jiti
vite
pnpm
webpack
socket.io
storybook
nx
ssh2
metro
react-native
esm
expo
tedious
electron
nuxt
cli
isomorphic-git
angular
emoji-picker-react
bitcoinjs-lib
editor
hardhat
gatsby
laravel-mix
node
ethereumjs-wallet
s3rver
quasar
bootstrap-vue
handsontable
create-react-app
hyperformula
summernote
remotion
vue-draggable-plus
documentation
umi
expo-cli
react-native-gifted-chat
vuepress
dumi
serverless-s3-local
pigeon-maps
react-streaming
antd-token-previewer
vue-beautiful-chat
docusaurus
patternfly
hooks
help
webtorrent
mpegts.js
server
node-sp-auth
vscode
flow
next.js
dev
kepler.gl
ncc
interface
web
honeybadger-js
vue-cli
react-native-big-calendar
lingo
scratch-gui
scratch-blocks
amplify-cli
danfojs
core
packages
ntlm-client
gridsome
create-nuxt-app
vue-cli-plugin-electron-builder
ant-design-pro
cht-conf
decap-cms
skeleton
react-ssr
nbdime
public
skulpt
jasypt
webpack-theme-color-replacer
neo4j-browser
table-sort-js
studio
log4brains
abi-to-sol
angular-cli
fast-cli
client
vuepress-theme-vdoing
neutrino
extension
v4
skpm
generator-joplin
tauri
e2e
drei
mastodon
youtube
ignite
mesh
mercury
books
h
buttplug
lib-jitsi-meet
verify-paddle-webhook
sp-rest-proxy
gulp-spsave
fengari-web
chromatic-cli
aws-sdk-js-v3
ionic-framework
balanceofsatoshis
joplin
ngx-tag-commander
frontend
development
examples
epub.js
ionic-cli
shap
navigate
md
monaco-tm
fnm
book
react-guitar
explorer
protobuf-decoder
react-demo
navbar
minio-js
s3cmd
jupyterlab
mms
website
videojs-hlsjs-plugin
create-elm-app
nodeppt
filebrowser
embed
editor.js
create-react-wptheme
vue-admin-template
reactgrid
td
issues
invoice-generator
node-http-ntlm
form-js
tooling
create-wasm-app
chooser
notebook
webwhiz
carrot2
jupyterlab-system-monitor
lumino
odin
smb-enumerate-shares
torrent-stream-server
webpacker
hs-airdrop
issue-status
jupiter
paraviewweb
react-pdf-editor
resume
solutions
lightdash
ares
brave-browser
icestark
bqplot-image-gl
ag-grid-react-example
frank
glance
phaser3-merged-input
react-juce
supersonic
tribute-contracts
create-apollo-app
faq
file-opener
fiora
neuroglancer
coffeehouse
touche
good-first-issue
slinky
che
dopamine
js-ipfs
qti3-item-player
tango
component-template
covalent
ethereum-boilerplate
rust-webpack-template
tailwindcss.com
blast
metaplex
react-example
solid-file-widget
deploy-v3
ipfs-blog
jupyter-ros
node-bin-gen
nym
otp-react-redux
react-fundamentals
stencil-cli
twill
weatherapp
aggr
ant-design-vue-pro
dashy
datahub
dev-portfolio
intro-storybook-react-template
kana
next-social
numworks.js
plutonium
pushkin
react-firebase-chat
rebus
starter-kit
taqueria
behavior3editor
best-resume-ever
camunda-modeler-token-simulation-plugin
hubs
jfxr
notus-react
nyaf
react-code
react-todo
root-config
rundeck
soundboard
spritemate
tur
viro
vue-chat
vue-web-extension
wifi-connect
workshop
argo-rollouts
better-chat
compose-multiplatform
coriolis
crossmint-sdk
cypress-realworld-app
dashvis
gatsby-starter-leaflet
github-action
lowcode-engine
marinara
nextjs-portfolio
nextly-template
nuxt-starter
react-native-website
shopware-cli
template-webpack
testbench
wasm_game_of_life
webcrate
whatsapp-clone
Note again that this is only a heuristic-driven slice of the problematic dependencies. You should still run through your project and test, where possible.
Nevertheless, I hope it can prove at least somewhat useful. There’s still work involved in figuring out what version you’ll need to upgrade each dependency to, and fixing up any breaking changes if those updates require a major version bump, but it’s a start.
Still stuck?
There are a lot more things that can go wrong than what we’ve listed here, and powering through all the work to resolve these upgrades and incompatibilities can be extraordinarily tedious. If you find yourself in need of extra help (or just have a question!) the experts at BHC would be happy to talk to you.