[{"data":1,"prerenderedAt":6},["ShallowReactive",2],{"post-content-node-26-lts-upgrade-breaking-changes":3},{"content":4,"lastModified":5},"\u003Cp>I skipped the Node 26 release notes for five months. Every summary opened with Temporal, and I use \u003Ccode>Date\u003C\u002Fcode> in the handful of places where \u003Ccode>Date\u003C\u002Fcode> is fine, so the release read like somebody else's problem.\u003C\u002Fp>\n\n\u003Cp>What got me to open the notes was the schedule. Node 24 drops to maintenance on 20 October, Node 26 becomes LTS on 28 October, and Node 22 stops receiving fixes next April. None of that cares whether you are excited about a new date API.\u003C\u002Fp>\n\n\u003Cp>So I spent an afternoon pointing the Node 26 changes at a real codebase and watching what moved.\u003C\u002Fp>\n\n\u003Cimg src=\"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1517694712202-14dd9538aa97?auto=format&amp;fit=crop&amp;w=1200&amp;q=80\" alt=\"A laptop on a white desk with an editor open on a JavaScript file, a small potted plant and a yellow mug beside it\" loading=\"lazy\" \u002F>\n\n\u003Ch2>Three greps, and the noise that nearly fooled me\u003C\u002Fh2>\n\n\u003Cp>The removals are the easy part, because each one left a string behind. \u003Ccode>http.Server.prototype.writeHeader()\u003C\u002Fcode> is gone, so use \u003Ccode>writeHead()\u003C\u002Fcode>. The six private stream modules are gone as well: \u003Ccode>_stream_wrap\u003C\u002Fcode>, \u003Ccode>_stream_readable\u003C\u002Fcode>, \u003Ccode>_stream_writable\u003C\u002Fcode>, \u003Ccode>_stream_duplex\u003C\u002Fcode>, \u003Ccode>_stream_transform\u003C\u002Fcode> and \u003Ccode>_stream_passthrough\u003C\u002Fcode>. And \u003Ccode>--experimental-transform-types\u003C\u002Fcode> is gone too, which matters if you run TypeScript straight off disk.\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">grep -rn \"writeHeader(\" app\u002F server\u002F --include=\"*.ts\" --include=\"*.js\"\ngrep -rn \"_stream_\" app\u002F server\u002F --include=\"*.ts\" --include=\"*.js\"\ngrep -rn \"experimental-transform-types\" package.json nuxt.config.ts .github\u002F\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>All three came back empty. The second grep is the one worth talking about. Drop the path from it and it lights up a dozen files: unenv's compatibility shims, the wrangler CLI bundle, some \u003Ccode>core.json\u003C\u002Fcode>, a few type definitions. None of them are the removed modules. They just mention the names.\u003C\u002Fp>\n\n\u003Cp>Narrowing the pattern to an actual \u003Ccode>require('_stream_readable')\u003C\u002Fcode> returns nothing, which is the opposite of what the loose grep reported. A grep that found a mention is not a grep that found a dependency. For a stack built this year, that is the whole removals section.\u003C\u002Fp>\n\n\u003Ch2>The list nobody prints: five deprecations promoted in 26.0.0\u003C\u002Fh2>\n\n\u003Cp>The second list is where the Saturday goes. Five deprecations changed type in 26.0.0, and four became runtime deprecations, so they now warn from inside \u003Ccode>node_modules\u003C\u002Fcode> too.\u003C\u002Fp>\n\n\u003Cp>One of the five is worse than a warning. DEP0182 went End-of-Life, the level Node uses for a feature that is gone or on its way out.\u003C\u002Fp>\n\n\u003Cp>DEP0182 covers short authentication tags in GCM mode. \u003Ccode>decipher.setAuthTag()\u003C\u002Fcode> used to accept a tag of any valid length, and that leniency is how plenty of services talk to systems that truncate a tag to 8 or 12 bytes. That exception is gone. A tag shorter than the cipher's default length now needs the length declared when you build the decipher.\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-javascript\">const decipher = crypto.createDecipheriv('aes-128-gcm', key, iv, {\n  authTagLength: 8,\n});\n\ndecipher.setAuthTag(tagFromTheOtherSystem);\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>What makes it nasty is how quiet the run-up was. DEP0182 was documentation-only in 20.13, and the runtime deprecation did not land until 23.0.0. If you are on 22, you are standing in the gap. I pushed an 8-byte tag through a 22.22.2 build and it decrypted fine, with nothing on stderr. There was never a warning to ignore. The first signal arrives on 26, as a failed decrypt.\u003C\u002Fp>\n\n\u003Cp>The other four are easier to grep for. Passing a \u003Ccode>CryptoKey\u003C\u002Fcode> into \u003Ccode>node:crypto\u003C\u002Fcode> functions is now a runtime deprecation, as is handing a non-extractable \u003Ccode>CryptoKey\u003C\u002Fcode> to \u003Ccode>KeyObject.from()\u003C\u002Fcode>; both turn up when you mix WebCrypto with the older crypto API. \u003Ccode>Duplex.toWeb()\u003C\u002Fcode> wants \u003Ccode>readableType\u003C\u002Fcode> where it used to take \u003Ccode>type\u003C\u002Fcode>. And \u003Ccode>module.register()\u003C\u002Fcode> is deprecated in favor of \u003Ccode>module.registerHooks()\u003C\u002Fcode>, which matters if a dependency ships a custom loader.\u003C\u002Fp>\n\n\u003Ch2>The ABI bump warning is narrower than it sounds\u003C\u002Fh2>\n\n\u003Cp>The writeups I read all say the same thing: \u003Ccode>NODE_MODULE_VERSION\u003C\u002Fcode> moved to 147, so your native addons need rebuilding. The number is correct. The 22.22.2 install on my laptop reports 127.\u003C\u002Fp>\n\n\u003Cp>But that is a claim about addons written against NAN or the raw V8 API, and those have been on the way out for years. Node-API exists so a compiled addon does not care which major you run. You can check which kind you have:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">for f in $(find node_modules -name \"*.node\"); do\n  printf '%s %s\\n' \"$f\" \"$(nm -gU \"$f\" | grep -c napi_register_module_v1)\"\ndone\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>Mine has six compiled binaries: sharp, sharp's libvips companion, lightningcss, rolldown, rollup and fsevents. Five of them export \u003Ccode>napi_register_module_v1\u003C\u002Fcode>, so the ABI change is a non-event here. The sixth is a file literally named \u003Ccode>stub.node\u003C\u002Fcode>.\u003C\u002Fp>\n\n\u003Cp>They also arrive as platform packages, \u003Ccode>@rolldown\u002Fbinding-darwin-arm64\u003C\u002Fcode> and friends, so nothing compiles at install time and no deploy image needs a toolchain. If your list is full of older addons instead, the warning applies, and the failure shows up as a compile step running for the first time on a machine that never needed one.\u003C\u002Fp>\n\n\u003Ch2>What I am doing before 28 October\u003C\u002Fh2>\n\n\u003Cp>The scan turned up something the release notes cannot tell you. My repo pins nothing. No \u003Ccode>engines\u003C\u002Fcode> field, no \u003Ccode>.nvmrc\u003C\u002Fcode>, no Dockerfile. The Node version comes from whatever the machine happens to have, which is why my laptop sits on 22 while the notes describe a world where 26 is the target.\u003C\u002Fp>\n\n\u003Cp>So the order is: pin the version, put the two greps and the \u003Ccode>authTagLength\u003C\u002Fcode> question into CI, then take 26 into staging, where the native binaries and the deploy image match production.\u003C\u002Fp>\n\n\u003Cp>One more reason to land on 26 rather than wait. It is the last release on the twice-a-year model. From 27 the project ships one major a year and every release becomes LTS, with the alpha channel opening on 28 October, the same day 26 goes long-term. Moving now puts you on a line that runs to April 2029.\u003C\u002Fp>\n\n\u003Cp>What I wanted five months ago was the deprecation table read in order, with someone marking the rows that throw. The greps live in \u003Ca href=\"\u002Fsnippetark\u002F\">Snippet Ark\u003C\u002Fa> now, next to the \u003Ca href=\"\u002Fposts\u002Fnode-type-stripping-without-ts-node\u002F\">type stripping notes\u003C\u002Fa> on what replaces \u003Ccode>--experimental-transform-types\u003C\u002Fcode>. Same habit as the \u003Ca href=\"\u002Fposts\u002Fmysql-8-0-to-8-4-upgrade-gotchas\u002F\">MySQL 8.0 to 8.4 move\u003C\u002Fa>: release notes are a specification, and reading them as a list of features is how you find out about the rest in production.\u003C\u002Fp>\n","2026-10-09",1791775771994]