Проведём мысленный эксперимент. Предположим, требуется спроектировать и реализовать софт, который:

  1. Проживёт 10+ лет (совсем не редкость в ряде сфер) ядром, продолжая собираться и работать без изменений на новом тулчейне.
  2. Должен как можно менее болезненно переноситься на разное железо (просто потому, что с годами у вас могут меняться бюджет и законодательство).
  3. Должен максимально оптимально утилизировать железо (что позволит снизить стоимость продукта).
  4. Иметь как можно меньшие технологические окна (образно говоря, обновиться за минуту, а не за час), что важно для труднодоступного железа, для коротких сеансов связи, для крайне дорогого оборудования (минута простоя которого лишает вас обеда), для жизненно важного оборудования (в вашей груди кардиостимулятор, какой даунтайм ему позволите?).
  5. Должен пройти множество разных сертификаций на безопасность, соответствие требованиям и т.п. как человеком, так и автоматикой.

Ну и хватит, пожалуй. Идеальным примером (под который я подшлифовал требования, конечно) будет АМС «New Horizons», про которую читаю в интереснейшей [Стерн А., Гринспун Д. За новыми горизонтами. Первый полет к Плутону. Альпина нон-фикшн, 2020]. И… всё чтение не покидает желание примерить Node.js к этим требованиям. А почему бы и нет? Послушать адептов, так всё хорошо. Это раньше было кхм, а нынче-то ну прям всё есть. А уж какие сервера фронтендеры пишут, так хоть в пояс Койпера отправляй косяками, ни один не подведёт.


Во-первых, если язык JavaScript в какой-то мере фиксируется стандартами, то платформа Node.js развивается безо всяких стандартов. Как, впрочем, и её движокV8. Обратите внимание – речь идёт про систему из трёх элементов, каждый из которых живёт своей собственной жизнью в рамках разных процессов. Спрогнозировать состояние этой системы через два-три года… мне кажется, импосибиру.

Во-вторых, npm Dependency Hell. Это когда вы выполняете npm install coolpackage, а в node_modules ставится тысяча пакетов (ну вот типичное), 9/10 кода которых вам не надо. Более того, невинное обновление какой-нибудь библиотеки может вызвать лавину сопутствующих обновлений, некотролируемо (разве что вообще не обновляться) приносящих в систему новый хаос (что там накодил Васян, однострочный пакет которого подцепил двустрочник Игоря?).

В-третьих, [re]deploy и rollback Node.js-приложения как был головной болью, так и остался. Вместо того, чтобы в идеале заменить один исполняемый файл на другой (условно секундное дело), приходится планировать общевойсковую операцию. А уж если при этом ещё и Node.js / npm обновлять, так вообще веселье. Повторю мысль, красной нитью проходящую через весь блог: никого в этом мире не колышет, как долго будет валяться ваша страничка про кота или ваша стотысячная социальная сеть для зеленоглазых подростков Мухочешенского округа. Хоть на час уходите в «на сайте ведутся технические работы». Совсем другое дело космические станции, кардиостимуляторы, оборудование ГЭС, чугунолитейного завода («у нас тут чёт пакетик продолбался, потому домна ща немножко остынет») и т.п.

В-четвёртых, работа с числами никакая. Напомню, основа JavaScript – 64-битный float, кошерность которого (НАМ ХВАТАЕТ) долго защищали, но внезапно (!) оказалось, что для снежинок на странице его хватает, а вот для тех же WebSocket уже нет (а также для аудио, видео, связи). Мир JavaScript медленно решается входить в мир, в котором числа содержатся в байтах – добавили типизированные массивы, также уже есть BigInt. Всё это полумеры и лечение симптомов, а не болезни, и как дальше дело повернётся, не понять. Как минимум, в исходниках Node.js / V8 любопытно изучать все эти цепочки превращений чисел для битовых операций, например. Максимальная утилизация железа получается, да, но оптимальной её не назвать.

В-пятых, многопоточность. И снова испокон веков адептам ХВАТАЛО исходной однопоточности (строго говоря, речь не про однопоточность самой Node.js / V8, но про доступные API), и снова внезапно (!) оказалось, что при переходе от снежинок на следующую ступеньку таки надо уметь N потоков (процессов, нитей, you name it). В этой области тоже не пойми что происходит: есть Cluster (в основе которого Child Process), есть Worker Threads. Экспериментально сделаны они полтора года назад, статус stable получили весной этого года, если не ошибаюсь. И… ладно, это субъективизим и наверняка есть фанаты такого варианта, но меня отстреливает уже то, что воркер создаётся через путь к файлу скрипта. Шедевр. Как всё это на реальных и декларируемых задачах работает, пока не понять, ну вот можно листать исследования вроде этого или этого. // блин, ну в самом деле, пробрасывать в child порт? не разрешать импортировать модуль в разных потоках (кто не сталкивался, вас однажды поджидает Module did not self-register)? уверен, снова будут адепты даже такого.

В-шестых, автоматический анализ кода. Кто не сталкивался, поясню: это когда на код натравливается не плагин к IDE, подчёркивающий неиспользуемые переменные, но что-то вроде Svace от ИСП РАН или PVS-Studio. После прохода таких инструментов по проекту спать можно если не совсем уж спокойно, то без ночных всхлипываний. Только вот для JavaScript подобного уровня анализаторов не существует, насколько знаю. И вряд ли они появятся, т.к. основные потребители – это компании (между строк читаем «коммерческая лицензия не по карману пионеру и даже комсомольцу»), а они до сих пор ничего серьёзного на JavaScript не пишут.


Список выше – стратегические травмы. То, что невозможно исправить, не изменив архитектуру и / или методологию разработки. Я сознательно не коснулся некоторых особенностей языка (в любом есть особенности, тут грех на JavaScript пальцем кивать, но почитать кого-то вроде Douglas Crockford рекомендую). Сознательно не коснулся вопроса универсальности (за людьми, уверяющими, что на JavaScript они прям всё-всё напишут и оно будет хорошо работать, скоро приедут крепкие уставшие люди с недобрыми лицами и жидкостью в шприцах). Не говорю, что на JavaScript вообще нельзя писать хороший софт (кроме очевидной браузерной ниши есть ниши, в которых Node.js отжигает).

Вопрос скорее в том, можно ли Node.js применять вне сложившихся ниш и допускать в сложные ответственные продукты, разрабатываемые в точные сроки? Как по мне, нельзя. Как минимум, вы не сможете доказать (метод «клянусь, у меня работает!» на суде могут не засчитать, учтите), что код… не «не содержит ошибок», конечно же, но… хотя бы проверяем, так скажу. И не мануальными терапевтами из отдела QA (прокликали кейс по тикету, ага, закрыли тикет, а то, что бомбануло в другом модуле, заметим уже после выкатки).


PS. После таких эссе часто приходят люди с вопросами класса «а как иначе». Например, вот иначе для КК «Orion»: C++ код GN&C генерируется моделями MATLAB/Simulink. Ну т.е. сначала вы доказываете, что модель правильная, потом по правильной модели генерируете код.