Работаю с Node.js каждый день. И каждый день вспоминаю старый добрый Express с теплотой. Вот что бесит больше всего в 2026 году.

1. Callback hell в нативных модулях

Звучит как 2015 год, но нет. Стандартные модули Node.js до сих пор местами построены на коллбеках. new AudioContext() требует callback. Half of fs functions still return callbacks instead of Promises. И даже если есть async-версии — они разбросаны хаотично, без системы.

Решение: использую утилиту promisifyAll из своего набора. Но хочется уже просто писать await fs.readFile() и чтобы это работало из коробки.

2. require() vs import — путаница с ESM

Два стандарта в одном языке. type: "module" в package.json решает проблему для новых проектов. Но libraries written in 2019 still use CommonJS and there's no way around it. Иногда нужно заворачивать require в динамический импорт и молиться, чтобы оно не сломалось при следующем обновлении.

Особенно больно, когда в одном проекте три версии одной библиотеки из-за того, что разные пакеты тянут разные зависимости.

3. Ошибки в async/await — без стектрейса

Поймать ошибку в async-функции — легко. Понять, откуда она пришла — нет. Node.js часто показывает просто "Error: something went wrong" без нормального стектрейса. Особенно если ошибка прилетает из стороннего пакета.

Мой рабочий костыль: оборачиваю всё в try/catch и пишу в лог полный контекст. Но это же не должно быть ручной работой.

4. process.env — Null/undefined путаница

В Python: os.getenv('VAR', 'default'). В Node.js: process.env.VAR || 'default'. Кажется, одно и то же. Но есть нюанс. Если VAR="" (пустая строка), то || вернёт default, хотя программист имел в виду пустую строку. И начинаешь получать странные баги в проде, где переменная "как бы есть, но как бы нет".

Плюс нет встроенной валидации типов. process.env всегда строка. Считай сам, парси сам, проверяй сам.

5. Модуль node:worker_threads — мощный, но неудобный

Worker threads в Node.js — это реально круто. Можно распараллелить тяжёлые вычисления. Но API для общения между воркерами — это боль. MessageChannel, SharedArrayBuffer, transfer list — всё это нужно изучать отдельно, и каждая задача требует индивидуального подхода.

Для простого «запустить функцию в другом потоке» приходится писать обёртки. Хочется что-то вроде Python multiprocessing.Pool — просто и понятно.

Что делать?

Я не жалуюсь. Node.js — это инструмент, и у любого инструмента есть недостатки. Просто полезно их знать. Большинство проблем решаются практикой и собственными утилитами. Главное — не пытаться бороться с фреймворком, а адаптироваться к нему.

А что раздражает вас в Node.js? Напишите в комментариях.