Технології
Node.js 25: що нового насправді, що зламалось в екосистемі, і чи варто оновлюватись
Живий розбір Node.js 25 (Current) з історіями з реального світу: V8 14.1 і перформанс, Web Storage увімкнули за замовчуванням (і це зламало тести/тулзи), permission model з --allow-net, portable compile cache (тепер stable), require(esm) став stable, http.setGlobalProxyFromEnv(), fs.watch отримав ignore, і SEA тепер збирається однією командою через node --build-sea. Думки розробників з лінками + чіткий висновок: апгрейдити чи чекати LTS.

Пролог: апгрейднули Node — і раптом «localStorage» валить збірку
Є два типи апгрейду Node. Перший — «підняли версію, все ок». Другий — «підняли версію, а наш проєкт тепер видає браузерні помилки на бекенді». Node.js 25 багатьом запам’ятався другим типом.
Кейс Docusaurus виглядав максимально сюрреалістично: DOMException [SecurityError]: Cannot initialize local storage without a --localstorage-file path — під час docusaurus build.[1] Аналогічні симптоми ловили і в CLI-світі (Shopify), і в тест-раннерах (Jest/Vitest).[2][3][4]
Це важливо: це не «рандомний баг у вашому репо». Це наслідок того, що Node 25 ввімкнув Web Storage за замовчуванням, а localStorage у Node — це не браузерний storage 1:1 (файл, квота, shared-поведінка, відсутність шифрування).[7][5]
І разом з цим у Node 25 є великий плюс: 25.4.0 і 25.5.0 принесли зрілі фічі (require(esm) stable, compile cache stable, --build-sea), які реально зменшують міграційний біль і покращують DX — але їх треба брати з розумом.[9][10][12]
Реальний кейс: docusaurus build падає на Node 25.x через localStorage SecurityError.[1]
Міні-хронологія Node 25: чому важливі не тільки «25.0.0», а й 25.4 та 25.5
Node 25 — це Current-лінія (odd). Вона живе швидко: спершу «включили нові дефолти», потім екосистема знаходить edge-cases, потім виходять стабілізаційні релізи.[6]
• 25.0.0: V8 14.1, акцент на перформанс, web-API, permission model, включення Web Storage by default.[8]
• 25.2.0: на поверхню вилізла регресія
localStorageбез--localstorage-file(nodejs/node #60704), яка зачепила тулінг-ланцюжки.[5]• 25.4.0: require(esm) стає stable, module compile cache стає stable, додають ops-плюшки на кшталт
http.setGlobalProxyFromEnv().[9]• 25.5.0: з’являється
node --build-sea— SEA «в один крок» із core, плюс дрібні, але важливі зміни для ops/monorepo.[12]
Швидка термінологія, щоб читати без «гугління кожні 2 хвилини»
Мінімум понять, без яких Node 25 читається як набір випадкових флагів і regression-тредів.
• Current vs LTS: odd major = Current, even major = LTS-лінія. Node прямо рекомендує production тримати на Active/Maintenance LTS.[6]
• Web Storage у Node:
localStorageу Node — файл-базований storage (--localstorage-file), незашифрований, квота 10MB, і в серверному процесі він shared.[7]• Permission Model: дозволи процеса (
--permission,--allow-net,--allow-fs-read) як «ремінь безпеки». Це не sandbox і не дає гарантій проти malicious code.[11]• require(esm): міст між CJS і ESM. Node 25.4.0 зафіксував стабільність механізму (і це впливає на міграції).[9]
• Module compile cache: on-disk code cache для пришвидшення компіляції модульного графа, з
portableрежимом.[10]• SEA: single executable applications. У 25.5.0 це стало значно простіше через
--build-sea.[12][13]• Ops QoL: проксі з env (
http.setGlobalProxyFromEnv()), ігнор у вотчерах (fs.watch({ ignore: ... })).[14][15]
Node 25.0.0: що нового «в базі» і де тут практична користь
25.0.0 — це фундамент: V8 14.1, перформансні штуки, web-API, і підчищення legacy. Але справжня “зрілість” лінійки відчувається в 25.4/25.5.[8][9][12]
Web Storage
Смілива default-зміна, яка дала хвилю регресій в екосистемі (тулінг/тести/CLI).[8][5][4]
Security posture
Permission model у фокусі «secure-by-default» підходу, але з чіткими обмеженнями (не sandbox).[11]
Legacy cleanup
Major-релізи часто болять не через фічі, а через прибирання deprecated API (приклад — SlowBuffer).[16]
Візуальний маркер початку лінійки: Node.js 25.0.0 (Current).[8]
Section node-25-0 screenshotWeb Storage by default: чому це вистрілило і як це виглядає в реальних проєктах
Проблема не в тому, що Web Storage — погана ідея. Проблема в тому, що змінилася default-поведінка, і частина тулінгу/бібліотек почала виконувати гілки коду, які ніколи не тестувалися на сервері.
nodejs/node #60704 прямо описує регресію 25.2.0: «Cannot initialize local storage without a --localstorage-file path». І важливий момент: issue посилається на зламані ланцюжки webpack/jest/html-webpack-plugin.[5]
Vitest issue #8757 показує інший тип поломки: у Node 25 localStorage перестає бути undefined, і це може “збити” моки у тестових середовищах.[4] Jest issue #15888 — ще один доказ, що це не поодинокий кейс: тест-раннер падає з SecurityError на Node 25.2.0.[3]
Швидкий антикризовий workaround (коли storage вам не потрібен) — тимчасово вимкнути web storage флагом, який описаний у docs.[7]
Ключова думка для команд: навіть якщо ви «не використовуєте localStorage», ваші залежності можуть робити це опосередковано. Тому Current-лінію логічно тримати в CI-матриці як ранній детектор таких сюрпризів.[6]
nodejs/node #60704: регресія 25.2.0 — localStorage без --localstorage-file провокує падіння.[5]
Vitest #8757: Node v25 вмикає Web Storage, і тести можуть ламатися через зміну очікувань щодо localStorage.[4]
Jest #15888: тест-раннер падає з SecurityError на Node 25.2.0.[3]
Node 25.4.0: require(esm) стає stable (і це реально міняє міграції)
Це той клас змін, який не «кричить» у реліз-нотах, але економить тижні в репозиторіях, де CJS і ESM змушені співіснувати.
1) Чому це важливо
2) Практичний патерн: адаптер під default export
const pkg = require('some-esm-only-package');
const api = pkg?.default ?? pkg;
module.exports = api;Це не “хак”. Це типовий спосіб працювати з namespace-object, коли ESM експортує default.[22]
3) Сигнал для великих команд
Стабілізація в Current-лінії — це підготовка до того, що з часом поведінка стане звичною і для LTS-світу. Тому CI-матриця (LTS+Current) тут — найздоровіший спосіб забрати користь без ризику інцидентів.[6]
Візуальний маркер “стабілізаційного” релізу: Node.js 25.4.0 (Current).[9]
Section require-esm screenshotModule compile cache: portable режим і “доросла” документація — що це дає в CI/контейнерах
Compile cache — це оптимізація, яка найкраще працює там, де у вас багато холодних стартів або великий модульний граф (особливо інструменти, CLI, воркери).
У доках node:module є дуже практичний розділ про переносимість: якщо змінюються absolute paths, кеш може втратити ефективність; portable режим дозволяє reuse у більшій кількості сценаріїв.[10]
Мінімальний приклад, який зазвичай достатній для експерименту:
import module from 'node:module';
module.enableCompileCache({ directory: '.node-compile-cache', portable: true });Якщо ви це тестуєте в CI, варто керувати директорією кешу явно (щоб не «випаровувався» між прогонами).
Docs: “Portability of the compile cache” + portable режим і env-приклад.[10]
Node 25.5.0: `node --build-sea` робить SEA «однією командою»
Це одна з найбільш «продуктових» змін лінійки: тепер збірка single-executable не виглядає як ритуал з зовнішнім інжектором.
1) Базова ідея
2) Практичний висновок
Для CLI/локальних агентів це означає менше залежностей і менше “крихких” кроків у release pipeline. Але як і все кросплатформене — тестуйте на цільових OS/шелах (є окремі репорти про Windows Command Prompt нюанси).[21]
3) Порада для команд
SEA — це інструмент дистрибуції, не заміна security-моделі. Не підмінюйте ним оновлення залежностей/перевірку supply chain. І тримайте CI-матрицю, бо саме вона ловить сюрпризи Current-ліній.[6]
Реліз 25.5.0: блок про --build-sea і приклад команд прямо в нотатках релізу.[12]
Ops QoL: проксі з env і зрозумілий шлях для корпоративних мереж
Це той тип змін, який не потрапляє в заголовки, але реально зменшує кількість «дрібних кастомних обгорток» у проєктах з корпоративним CI.
Мінімальний приклад:
import http from 'node:http';
const restore = http.setGlobalProxyFromEnv();
// restore();API
Зручно для середовищ, де проксі керується через env (CI/enterprise networks).[14]
Порада
Уникайте виклику “посеред запитів”; робіть це на bootstrap етапі сервісу/CLI.[14]
Сумісність
Менше шансів, що кожен HTTP-клієнт буде налаштований по-своєму.
Docs: http.setGlobalProxyFromEnv([proxyEnv]) + секція про built-in proxy support.[14]
Monorepo-біль: `fs.watch({ ignore })` як простий спосіб зменшити шум
Якщо у вас великий репозиторій, вотчери — це окремий світ болю (node_modules, .git, генеровані директорії). Node 25.5.0 згадує доповнення до fs.watch, а docs описують ignore як опцію, яка може приймати glob/RegExp/function/array.[15]
Мінімальний приклад для dev-інструментів:
import { watch } from 'node:fs';
watch('.', {
recursive: true,
ignore: ['**/node_modules/**', '**/.git/**'],
}, () => {});Breaking changes: як Node 25 ламає залежності не фічами, а «прибиранням legacy»
У major-релізах Node часто боляче не через нові API, а через фіналізацію деprecation. SlowBuffer — показовий приклад такого прибирання.[16]
1) Що сталося з SlowBuffer
В deprecations docs Node прямо зазначено, що SlowBuffer прибрано і треба переходити на Buffer.allocUnsafeSlow(size).[16]
2) Як це виглядає “в полі”
Є реальний кейс, де бекенд падав на Node 25.x через dependency chain, який торкався SlowBuffer.prototype.[17]
3) Швидкий scan перед апгрейдом
rg -n "\bSlowBuffer\b" .Потім pnpm why/npm ls, щоб знайти, хто тягне legacy-залежність.
Upgrade playbook: як додати Node 25 так, щоб він давав користь, а не біль
Це короткий «бойовий» чекліст, який реально працює в командах.
• 1) Тримай прод на LTS, а Node 25 додай у CI (матриця LTS + Current).[6]
• 2) Проганяй тести з вимкненим Web Storage, щоб швидко локалізувати проблеми:
NODE_OPTIONS="--no-experimental-webstorage".[7]• 3) Якщо зламалося — дивись туди, де вже зламалось у інших: nodejs/node #60704, Vitest #8757, Jest #15888.[5][4][3]
• 4) Якщо тебе цікавлять фічі 25.4/25.5, винось їх у окремий експеримент (CLI/воркер), не в прод-бекенд “одним махом”.[9][12]
• 5) Для monorepo підкрути вотчери:
fs.watch({ ignore })знижує шум і навантаження.[15]• 6) Для корпоративного CI за проксі: спробуй
http.setGlobalProxyFromEnv()як просте глобальне рішення.[14]
Висновок: чи варто переходити на Node.js 25 вже зараз
Вердикт без фанатизму: Node 25 — дуже корисний як «індикатор майбутніх проблем» (CI), і корисний для окремих продуктів (CLI/агенти). Але для прод-бекенду LTS часто виграє саме стабільністю.
CI / сумісність
Node 25 швидко покаже регресії залежностей (Web Storage) і legacy cleanup (SlowBuffer), без ризику інциденту в проді.[5][16]
CLI / агенти
--build-sea (25.5.0) + дозрівання 25.4.0 — це реальний DX-профіт. Просто тестуйте на target OS/шелах.[12][21]
Психологічно
Current існує, щоб екосистема встигла адаптуватися. Якщо він ламає день — тримайте його в CI, а прод залишайте на LTS.[6]
Sources
Джерела в порядку появи у тексті (позиція в списку = номер [n]).
• Docusaurus issue #11545: build broken on Node 25.2.0 due to localStorage SecurityError Читати джерело ↗
• Shopify community: shopify-cli fails on Node 25.2.0 due to localStorage SecurityError Читати джерело ↗
• Jest issue #15888: Jest fails with localStorage error on Node 25.2.0 Читати джерело ↗
• Vitest issue #8757: Node v25 breaks tests with Web Storage API Читати джерело ↗
• nodejs/node issue #60704: Regression 25.2.0 — Cannot initialize local storage without --localstorage-file path Читати джерело ↗
• Node.js Releases: Current vs LTS policy (production guidance) Читати джерело ↗
• Node.js Globals docs: Web Storage semantics + disable flag Читати джерело ↗
• Node.js 25.0.0 release notes (V8 14.1, Web Storage default, performance highlights) Читати джерело ↗
• Node.js 25.4.0 release notes (require(esm) stable, compile cache stable, ops improvements) Читати джерело ↗
• Node.js node:module docs: module compile cache + portability Читати джерело ↗
• Node.js Permissions docs: seat belt approach (not a sandbox) Читати джерело ↗
• Node.js 25.5.0 release notes (--build-sea, other notable changes) Читати джерело ↗
• Node.js Single Executable Applications docs (--build-sea flow and config) Читати джерело ↗
• Node.js HTTP docs: http.setGlobalProxyFromEnv() Читати джерело ↗
• Node.js Deprecations docs: SlowBuffer removed + migration note Читати джерело ↗
• twentyhq/twenty issue: crash on Node 25 due to SlowBuffer removal via dependency chain Читати джерело ↗
• nodejs/Release issue #1113: cadence discussion (annual majors / LTS duration) Читати джерело ↗
• nodejs/node issue: --build-sea fails in Windows Command Prompt (shell-specific behavior) Читати джерело ↗
• Joyee Cheung: require(esm) in Node.js — from experiment to stability Читати джерело ↗
• Joyee Cheung: Improving SEA building (why build moved into Node.js core) Читати джерело ↗
Часті запитання
Ні. Це Current (odd major). Node прямо пише, що production застосунки мають використовувати Active або Maintenance LTS.[6]
Бо змінилася default-поведінка. `localStorage` став доступний без experimental-флага, а в Node він має іншу семантику (файл, 10MB, shared). Це зламало припущення тулінгу/тестів.[7][5][4]
Тимчасово вимкнути Web Storage через `NODE_OPTIONS="--no-experimental-webstorage"` або як флаг запуску. Це згадується в globals docs і фігурує в regression-тредах як workaround.[7][5]
Так, але тестуйте на цільових OS/шелах. Є shell-specific issue на Windows Command Prompt.[21] Деталі конфігів і процесу — в SEA docs.[13]
Якщо коротко: забери користь з Node 25, але не бери ризик у прод “в лоб”
Найкраща стратегія для більшості команд: прод на LTS, Node 25 у CI як ранній детектор регресій, і окремі експерименти для фіч 25.4/25.5 (require(esm), compile cache, build-sea). Так ти отримуєш DX-профіт без “дня, який зламав localStorage”.