Дэвид Хэррон, автор материала, перевод которого мы публикуем сегодня, задался следующим вопросом: «Должен ли человек, работавший более 10 лет в Sun Microsystems, в команде Java SE, до последнего вздоха думать лишь о байт-коде Java и создавать экземпляры абстрактных интерфейсов?». Он задавал этот вопрос применительно к себе, и для него платформа Node.js, после Java, оказалась подобна глотку свежего воздуха. Дэвид говорит, что когда он был уволен из Sun в январе 2009 года (прямо перед поглощением этой компании Oracle), он узнал о Node.js. Эта технология его зацепила. Что значит «зацепила»? С 2010-го года он много писал о программировании для Node.js. А именно, написал несколько книг, в том числе — «Node.js Web Development», четвёртое издание которой вышло в этом году. Он подготовил множество небольших материалов о Node.js, опубликованных в интернете. Фактически, он уделил очень много времени и сил, рассказывая о платформе Node.js и о возможностях JavaScript. Почему того, кто раньше занимался исключительно Java, так увлекли Node.js и JavaScript?
Работая в Sun, я верил в технологию Java. Я делал доклады на JavaONE, участвовал в разработке класса java.awt.Robot, занимался организацией мероприятия Mustang Regressions Contest (это был конкурс, направленный на поиск ошибок в Java 1.6), помогал в запуске проекта «Distributions License for Java», который служил ответом на вопрос о Linux-дистрибутивах JDK до появления OpenJDK. Позже я играл некоторую роль в запуске проекта OpenJDK. Попутно я, в течение примерно 6 лет, публиковал материалы в блоге на java.net (теперь этот сайт закрыт). Это были 1-2 статьи в неделю, посвящённые значимым событиям в экосистеме Java. Значительную роль в моей деятельности играла защита Java от тех, кто предрекал этой технологии мрачное будущее.
Эту награду, Duke Award, давали особо отличившимся сотрудникам Sun. Мне она досталась после того, как я организовал Mustang Regressions Contest
Что произошло с человеком, который так много занимался всем тем, что связано с Java? Собственно говоря, тут я и хочу рассказать о том, как я превратился из приверженца Java в горячего сторонника Node.js и JavaScript.
Надо сказать, что то, что со мной произошло, нельзя назвать полным отказом от Java. Я, за последние 3 года, написал довольно много кода на Java, пользовался Spring и Hibernate. Хотя то, что я теперь делаю в этой области, мне очень нравится (я работаю в индустрии солнечной энергетики, занимаюсь тем, чем мне заниматься приятно, например — пишу запросы для работы с данными из сферы энергетики), программирование на Java в моих глазах теперь лишилось былого великолепия.
Два года разработки с использованием Spring позволили мне чётко уяснить одну важную вещь: попытка скрытия сложных механизмов не приводит к простоте, она лишь ведёт к появлению ещё более сложных конструкций.
Вот, вкратце, основные идеи, которых я коснусь в этом материале:
Некоторые инструменты или объекты являются результатом многолетних усилий инженеров по их совершенствованию. Программисты испытывают разные идеи, убирают ненужные атрибуты, и в результате у них получаются сущности, в которых имеется исключительно то, что нужно для решения некоей задачи. Часто таким технологиям присуще нечто вроде весьма привлекательной простоты, скрывающей мощные возможности. К Java это не относится.
Spring — это популярный фреймворк для разработки веб-приложений, основанных на Java.
Основная цель Spring, и, в частности, Spring Boot, заключается в предоставлении возможности пользоваться заранее настроенным стеком Java EE. Программист, который пользуется Spring, не должен, для того, чтобы создать готовую систему, заботиться о сервлетах, о системах постоянного хранения данных, о серверах приложений, и ещё неизвестно о чём. Все эти заботы перекладываются на плечи Spring, а программист занимается написанием кода, реализующего логику приложения. Например, механизмы JPARepository ответственны за генерирование запросов к базам данных для методов, названия которых выглядят как
Звучит всё это очень хорошо, работать в таком стиле приятно, но — до тех пор, пока не случится какая-нибудь неожиданность.
Я имею в виду ситуацию, когда, например, приходит исключение Hibernate
Далее, тут же можно вспомнить о длиннейших трассировках стека. Они представляют собой несколько экранов, полных всяких абстрактных методов. Spring, очевидно, создаёт конфигурацию, необходимую для реализации того, что выражено в коде. Подобный уровень абстракции, несомненно, требует немалого объёма вспомогательной логики, которая направлена на то, чтобы обнаружить всё необходимое для работы кода, например, для того, чтобы выполнять запросы. И длинные трассировки стека — это не обязательно плохо. Подобные вещи являются, скорее, симптомом, наводят на вопрос о том, какую нагрузку на систему создают вспомогательные механизмы.
Как выполняется метод
После того, как вам придётся несколько десятков раз пройти через нечто вроде поиска смысла вышеописанной ошибки, тратя недели на то, чтобы разгадывать секреты, которые, по большому счёту, вы разгадывать не должны, вы можете прийти к тому же заключению, к которому пришёл я. Смысл его состоит в том, что попытка скрытия сложных механизмов не приводит к простоте, она лишь ведёт к появлению ещё более сложных конструкций. Платформа Node.js устроена гораздо проще.
Слоган «Compatibility Matters» скрывал в себе замечательную идею, в соответствии с которой важнейшей особенностью платформы Java была обратная совместимость. Мы относились к этому серьёзно, нанося на футболки изображения подобные тому, которое вы можете видеть ниже.
Обратная совместимость — это очень важно
Конечно, подобный уровень внимания к обратной совместимости может быть источником постоянной тревоги, и время от времени полезно уходить от старых механизмов, которые больше не приносят пользы.
Spring и Java EE чрезмерно усложнены. Платформа Node.js на их фоне воспринимается как глоток свежего воздуха. Первое, на что обращаешь внимание, знакомясь с Node.js — это подход Райана Даля к разработке ядра платформы. Его опыт подсказал ему, что платформы, использующие потоки, нужны для создания сложных, тяжеловесных систем. Он же искал чего-то другого, и потратил пару лет на совершенствование набора базовых механизмов, воплощённых в Node.js. В результате у него получилась легковесная система, которую характеризует один поток выполнения, изобретательное использование анонимных функций JavaScript в роли асинхронных коллбэков, и библиотека времени выполнения, оригинально реализующая асинхронные механизмы. Изначальным посылом при создании такой системы было обеспечение высокой производительности обработки событий с доставкой этих событий в функции обратного вызова.
Далее, важной особенностью Node.js является использование JavaScript. Возникает такое ощущение, что у тех, кто пишет на JS, имеется склонность к избавлению от шаблонного кода, что позволяет чётко описывать намерения программиста.
В качестве примера различия между Java и JavaScript рассмотрим реализацию функций-слушателей (наблюдателей). В Java для работы со слушателями нужно создать конкретный экземпляр абстрактного интерфейса. Это влечёт за собой использование громоздких языковых конструкций, которые скрывают суть происходящего. Как разглядеть намерение программиста, скрытое под покровами шаблонного кода?
В JavaScript вместо этого используются простые анонимные функции. Реализуя слушатель, не нужно искать подходящий абстрактный интерфейс. Достаточно, без необходимости пользоваться множеством вспомогательных текстов, написать необходимый код.
Итак, вот одна важная идея, которую можно вынести из анализа вышеописанных механизмов: большинство языков программирования скрывают намерения программиста, что приводит к тому, что код оказывается сложным для понимания.
Решение, касающееся использования функций обратного вызова, которое предлагает Node.js, выглядит весьма привлекательно. Но и оно не лишено проблем.
В JavaScript издавна существовали две проблемы, связанные с асинхронным программированием. Первая — это то, что в Node.js называется адом коллбэков. Заключается эта проблема в том, что, в ходе разработки, легко попасть в западню, построенную из глубоко вложенных функций обратного вызова, где каждый уровень вложенности усложняет программу, а также обработку результатов работы кода и ошибок. Существовала и ещё одна проблема, связанная с этой, суть которой в том, что языковые механизмы JavaScript не помогали программисту должным образом выражать идеи асинхронного выполнения кода.
Для упрощения асинхронной разработки на JS возникли несколько библиотек. Но это — очередной пример попытки скрытия сложных механизмов которая, ведёт лишь к появлению ещё более сложных конструкций.
Рассмотрим пример:
Это — невзрачная имитация команды Unix
В сущности, этот код содержит цикл. Он не написан как обычный цикл, здесь не используются естественные конструкции описания циклов. Далее, результаты выполнения кода и выдаваемые им ошибки не попадают туда, куда им правильно было бы попадать. Они оказываются запертыми в коллбэках, а это неудобно. Но, до появления в Node.js реализации стандартов ES2015/2016, ничего лучшего сделать было нельзя.
Если переписать этот код с учётом новых возможностей, которые, в частности, имеются в Node.js 10.x, то получится следующее:
В этом примере мы воспользовались конструкцией
Единственный недостаток заключается в том, что
Теперь можно сделать вывод о том, что проблема ада коллбэков в JavaScript была решена способом, который отличается от попытки скрытия сложных механизмов. Вместо этого внесены изменения в язык, что решило и саму проблему, и избавило нас от неудобств, вызванных необходимостью использовать большие объёмы шаблонного кода во временном решении. Кроме того, с применением механизма async/await код попросту стал красивее.
Этот раздел мы начинали с обсуждения недостатка Node.js, но отличное решение проблемы ада коллбэков привело к тому, что разговор о недостатках превратился в разговор о сильных сторонах Node.js и JavaScript.
В те времена, когда я занимался защитой Java от разного рода нападок, я напирал на то, что строгая типизация позволяет писать огромные приложения. В те времена в ходу была разработка монолитных систем (не было микросервисов, не было Docker и тому подобных вещей). Так как Java является языком со строгим контролем типов, компилятор Java помогает программисту избегать множества проблем, не давая ему скомпилировать неправильный код.
JavaScript, в отличие от Java, не отличается строгой типизацией. Отсюда можно сделать очевидный вывод о том, что программист точно не знает, с какими именно объектами ему приходится работать. Откуда программисту узнать, что делать, например, с неким полученным откуда-нибудь объектом?
Обратная сторона строгой типизации Java — это необходимость в постоянном выполнении шаблонных действий. Программист постоянно производит приведение типов или занимается проверкой того, чтобы всё было в точности так, как ожидается. Разработчик тратит время на написание кода, делает это с исключительной точностью, использует немалые объёмы шаблонных конструкций, и надеется на то, что всё это поможет ему сэкономить время за счёт раннего обнаружения и исправления ошибок.
Проблема программирования на языке со строгой типизацией столь велика, что программист, практически без вариантов, должен использовать большую сложную IDE. Простого редактора кода тут недостаточно. Единственный способ поддержания Java-программиста в адекватном состоянии (за исключением пиццы) заключается в постоянном показе ему выпадающих списков, содержащих доступные поля объектов или описания параметров методов. Этот и другие вспомогательные механизмы таких IDE, как Eclipse, NetBeans, или IntelliJ, помогают в деле создания классов, облегчает рефакторинг и решение других задач.
И… не буду говорить о Maven. Это — просто кошмарный инструмент.
В JavaScript типы переменных не указывают при их объявлении, приведение типов обычно не используется, и так далее. В результате код легче читать, но такое положение дел означает и риск возникновения ошибок программирования, которые трудно обнаружить.
Относится ли вышеизложенное к плюсам Java или к минусам — зависит от точки зрения.
Десять лет назад я считал, что все эти сложности оправдывают себя тем, что дают программисту большую уверенность в коде, который он пишет. Сегодня я считаю, что строгая типизация увеличивает объём работы программиста и проекты гораздо легче разрабатывать так, как это делается в JavaScript.
Node.js подталкивает программиста к тому, чтобы он разбивал свои проекты на небольшие фрагменты, на так называемые модули. Возможно, этот факт покажется вам незначительным, но он частично решает только что упомянутую нами проблему.
Вот основные характеристики модуля:
Всё это делает Node.js-модули сущностями с чётко очерченными границами, код которых легко писать, читать и тестировать.
Однако беспокойство при работе с JavaScript вызывает тот факт, что отсутствие строгой типизации может легко привести к тому, что код сделает что-то не то. В маленьком модуле, нацеленном на решение какой-то узкой задачи, обладающем чёткими границами, «что-то не то» может затронуть лишь код самого модуля. Это приводит к тому, что проблемы, которые может вызвать отсутствие строгой типизации, оказываются запертыми в границах модуля.
Ещё одно решение проблемы динамической типизации в JavaScript заключается в тщательном тестировании кода.
Разработчику приходится серьёзно подходить к тестированию, что отнимает у него часть выгод, которые исходят из простоты процесса разработки на JS. Системы тестирования, создаваемые JS-программистом, должны находить те ошибки, которые, разрабатывай он на чём-то вроде Java, мог бы автоматически находить компилятор. Вы ведь пишете тесты для своих JS-приложений?
Тем, кому нужна система статической типизации в JavaScript, может быть полезно взглянуть на TypeScript. Я не пользуюсь этим языком, но мне доводилось слышать о нём много хорошего. Он совместим с JavaScript и расширяет язык системой контроля типов и другими полезными возможностями.
В итоге можно сказать, что использование модульного подхода к разработке — это сильная сторона Node.js и JavaScript.
Мне плохо от одной мысли о Maven, поэтому я не могу даже нормально писать о нём. И, насколько я понимаю, Maven, без компромиссов, либо любят, либо ненавидят.
Проблема тут заключается в том, что в среде Java нет целостной системы для управления пакетами. Пакеты Maven существуют, с ними можно нормально работать, их поддерживает Gradle. Но то, как организована работа с ними, и близко не похоже на те удобства, которые даёт разработчику система управления пакетами для Node.js.
В мире Node.js существуют два отличных менеджера пакетов, которые работают в тесной связи друг с другом. Сначала единственным подобным инструментом был репозиторий npm и одноимённый инструмент командной строки.
Благодаря npm в нашем распоряжении имеется отличная схема для описания зависимостей пакетов. Зависимости могут быть строгими (скажем, указывается, что нужна исключительно версия 1.2.3 некоего пакета), или заданными с несколькими степенями свободы — вплоть до
Сообщество Node.js опубликовало в репозитории npm сотни тысяч пакетов. При этом использовать пакеты, которых нет в npm, так же легко, как и пакеты из npm.
Система npm получилась настолько удачной, что пользуются ей не только разработчики серверных продуктов на Node.js, но и программисты, занимающиеся фронтендом. Раньше там, для управления пакетами, использовались инструменты вроде Bower. Bower был признан устаревшим, и теперь можно обнаружить, что все JS-библиотеки для разработки фронтенда существуют в виде npm-пакетов. Многие вспомогательные инструменты для клиентской разработки, вроде Vue.js CLI и Webpack, написаны в виде Node.js-приложений.
Ещё одна система управления пакетами для Node.js, yarn, загружает пакеты из репозитория npm и использует такие же конфигурационные файлы. Основное преимущество yarn перед менеджером пакетов npm заключается в более высокой скорости работы.
Репозиторий npm, независимо от того, работают ли с ним с помощью менеджера пакетов npm или c помощью менеджера пакетов yarn, представляет собой мощную основу того, что делает разработку для Node.js такой простой и приятной.
Однажды, после того, как я помогал в разработке java.awt.Robot, я вдохновился на создание этой вот штуки. В то время как официальное изображение Duke состоит из кривых, RoboDuke построен из прямых линий. Только локтевые суставы у этого робота круглые
И Java, и JavaScript критиковали за их невысокую производительность. В обоих случаях компилятор преобразует исходный код программы в байт-код, выполняемый на виртуальной машине, реализованной для конкретной платформы. Виртуальная машина, в свою очередь, преобразует байт-код в машинный код с использованием различных оптимизаций.
И у Java, и у JavaScript есть причины стремиться к высокой производительности. Если говорить о Java и о Node.js, то их роднит стремление к быстрому серверному коду. В случае с браузерным JavaScript стимулом к высокой производительности является повышение качества клиентских приложений. Мы поговорим об этом в разделе о насыщенных интернет-приложениях.
JDK Sun/Oracle использует HotSpot — виртуальную машину, поддерживающую множество стратегий компиляции байт-кода. Название этой виртуальной машины намекает на технику оптимизации, в ходе реализации которой обнаруживается код, который выполняется чаще всего, и к такому коду, чем более интенсивно он используется, применяется всё больше оптимизаций. HotSpot — это высокооптимизированная система, которая производит очень быстрый код.
Если говорить о JavaScript, то раньше мы задавались вопросом о том, как можно ожидать от JS-кода, выполняемого в браузере, возможностей, необходимых для реализации каких-либо сложных приложений. Считалось, что на браузерном JavaScript практически невозможно было бы создать нечто вроде набора традиционных офисных приложений. Сегодня для доказательства того, что это возможно, далеко ходить не надо. Я, например, пишу этот материал в Google Docs, и производительность меня полностью устраивает. Скорость работы браузерного JS улучшается с каждым годом.
Node.js следует в том же направлении, так как он использует движок V8 браузера Google Chrome.
В качестве примера можно привести это выступление Питера Маршалла, инженера Google, который занимается работой над V8, и основной задачей которого является улучшение производительности этого движка. Здесь он рассказывает о том, почему V8 перешёл с Crankshaft на Turbofan.
Машинное обучение — это область, в которой используются тяжёлые вычисления, для выполнения которых обычно пользуются языками R или Python. Машинное обучение, как и некоторые другие области, нуждаются в средствах для быстрого выполнения численных вычислений. Здесь JavaScript, по разным причинам, не особенно силён, но сейчас ведётся работа по созданию стандартизированной библиотеки для организации численных вычислений на JavaScript.
Из этого видео можно узнать об использовании в JavaScript новой библиотеки, TensorFlow.js. API этой библиотеки похоже на API TensorFlow для Python, она поддерживает импорт предварительно обученных моделей. Эту библиотеку можно использовать, например, для анализа видео с целью распознавания объектов, при этом все необходимые вычисления выполняются в браузере.
Вот выступление Криса Бэйли из IBM, где он затрагивает вопросы производительности и масштабируемости Node.js, в частности, при использовании конфигураций, основанных на Docker/Kubernetes. Он начинает рассказ с рассмотрения набора бенчмарков, которые демонстрируют значительно более высокую производительность Node.js в сравнении со Spring Boot. Речь идёт о пропускной способности подсистемы ввода-вывода, о времени запуска приложения и о потреблении памяти. Более того, от релиза к релизу производительность Node.js серьёзно улучшается, отчасти, благодаря улучшениям, вносимым в V8.
Здесь Бэйли говорит, что Node.js не подходит для выполнения интенсивных вычислений. Нам важно понять причину подобной рекомендации. Из-за того, что в Node.js используется однопоточная модель выполнения кода, длительные вычисления блокируют обработку событий. Я, в моей книге «Node.js Web Development», затрагиваю эту проблему, приводя три подхода к её решению:
Если производительности JavaScript для ваших задач недостаточно, взгляните на следующие два способа интеграции нативного кода в Node.js. Самый простой способ — это использование нативных Node.js-модулей. В наборе вспомогательных инструментов для Node.js-разработки имеется средство
WebAssembly позволяет компилировать программы, написанные на разных языках, в подмножество JavaScript, отличающееся очень высокой скоростью выполнения. WebAssembly представляет код, который выполняется внутри JavaScript-движка. В этом видео дан хороший обзор технологии и показано использование WebAssembly в среде Node.js.
Насыщенные интернет-приложения (Rich Internet Applications, RIA) были у всех на слуху лет десять назад. Тогда говорили о том, что они, реализованные на базе быстрых (для своего времени) JS-движков, способны сделать неактуальными традиционные настольные приложения.
На самом деле, эта история началась более 20 лет назад. Sun и Netscape договорились об использовании Java-апплетов в Netscape Navigator. JavaScript был, отчасти, разработан как скриптовый язык для Java-апплетов. Тогда индустрия надеялась на то, что на серверах будут использоваться Java-сервлеты, на клиентах — Java-апплеты. В результате разработчики попадут в замечательную ситуацию, когда и для серверов и для клиентов можно будет писать на одном и том же языке. Этого не случилось по разным причинам.
Десять лет назад JavaScript оказался достаточно мощным для того, чтобы сложные приложения можно было бы реализовывать исключительно его средствами. В результате появился модный тогда термин RIA, и ожидалось, что насыщенные интернет-приложения убьют Java в виде платформы для клиентских веб-приложений.
Сегодня мы начинаем видеть, как идея RIA начала приносить плоды. Благодаря Node.js стала реальной та желанная для многих ситуация, когда, и на сервере и на клиенте, используется один и тот же язык. Только теперь это JavaScript.
Вот несколько примеров:
Технология Java, в роли платформы для разработки настольных приложений, умерла не из-за насыщенных интернет-приложений, написанных на JavaScript. Она умерла, преимущественно, из-за невнимания к клиентским технологиям в Sun Microsystems. В центре внимания Sun были корпоративные пользователи, которым нужна высокая производительность серверных приложений. Я видел всё это своими глазами. Настоящим убийцей Java-апплетов стала проблема в безопасности, выявленная несколько лет назад в плагине Java и в Java Web Start. Это привело ко всемирному сокращению использования Java-апплетов и Webstart-приложений.
Другие виды настольных приложений всё ещё можно разрабатывать на Java, и, как результат, IDE NetBeans и Eclipse всё ещё борются друг с другом. Однако в этой сфере применения Java наблюдается застой, и за пределами инструментов разработки существует очень немного приложений, основанных на Java.
Исключением является технология JavaFX.
Технология JavaFX, 10 лет назад, планировалась как ответ Sun на появление iPhone. Планировалось, что эта технология позволит разрабатывать на платформе Java, доступной на мобильных устройствах, приложения, обладающие насыщенным графическим интерфейсом. Это позволило бы одним махом вывести из игры Flash и средства разработки приложений для iOS. Но ничего такого не произошло. JavaFX используется до сих пор, но эта технология не видела столь масштабного взлёта, на который рассчитывали её создатели. В наши дни всеобщее внимание притягивают веб-фреймворки вроде React, Vue.js и им подобных.
В описанной ситуации JavaScript и Node серьёзно обогнали Java.
Вот фотография кольца Java, которое как-то раздавали на конференции JavaONE. Такие кольца содержали чип с полной реализацией Java на борту. Их, в основном, использовали для разблокировки компьютеров, установленных в вестибюле.
Кольцо Java
Инструкция к кольцу
В наши дни разработчикам серверных проектом есть из чего выбирать. Индустрия больше не ограничена так называемыми «P-языками» (Perl, PHP, Python) и Java, так как теперь есть платформа Node.js, есть языки Ruby, Haskell, Go, Rust, и многие другие. В результате у серверных программистов теперь есть просто огромный выбор замечательных технологий.
Если же говорить о том, почему я, человек, который жил исключительно Java, перешёл на сторону Node.js, то ясно, что меня привлекла свобода, которая характерна для Node.js-разработки. Экосистема Java превратилась в обузу, а при использовании Node.js ничего такого не ощущается. Конечно, если мне, по работе, придётся писать на Java, я выполню эту задачу.
У каждого приложения имеются собственные нужды. И, безусловно, неправильно всегда и для всего использовать исключительно Node.js только из-за того, что эта платформа кому-то нравится. Выбор того или иного языка или фреймворка должен определяться техническими соображениями. Например, недавно мне пришлось работать с XBRL-документами. Так как лучшие библиотеки для работы с XBRL написаны на Python, для того, чтобы ими пользоваться, надо знать Python. Поэтому, выбирая технологии, нужно здраво оценивать реальные задачи проектов и останавливаться именно на том, что лучше всего подходит для решения этих задач.
Уважаемые читатели! Если вы, как и автор этой статьи, перешли на JavaScript с какого-то другого языка, или сменили какую-нибудь серверную платформу на Node.js, просим в двух словах об этом рассказать.
О мире Java
Работая в Sun, я верил в технологию Java. Я делал доклады на JavaONE, участвовал в разработке класса java.awt.Robot, занимался организацией мероприятия Mustang Regressions Contest (это был конкурс, направленный на поиск ошибок в Java 1.6), помогал в запуске проекта «Distributions License for Java», который служил ответом на вопрос о Linux-дистрибутивах JDK до появления OpenJDK. Позже я играл некоторую роль в запуске проекта OpenJDK. Попутно я, в течение примерно 6 лет, публиковал материалы в блоге на java.net (теперь этот сайт закрыт). Это были 1-2 статьи в неделю, посвящённые значимым событиям в экосистеме Java. Значительную роль в моей деятельности играла защита Java от тех, кто предрекал этой технологии мрачное будущее.
Эту награду, Duke Award, давали особо отличившимся сотрудникам Sun. Мне она досталась после того, как я организовал Mustang Regressions Contest
Что произошло с человеком, который так много занимался всем тем, что связано с Java? Собственно говоря, тут я и хочу рассказать о том, как я превратился из приверженца Java в горячего сторонника Node.js и JavaScript.
Надо сказать, что то, что со мной произошло, нельзя назвать полным отказом от Java. Я, за последние 3 года, написал довольно много кода на Java, пользовался Spring и Hibernate. Хотя то, что я теперь делаю в этой области, мне очень нравится (я работаю в индустрии солнечной энергетики, занимаюсь тем, чем мне заниматься приятно, например — пишу запросы для работы с данными из сферы энергетики), программирование на Java в моих глазах теперь лишилось былого великолепия.
Два года разработки с использованием Spring позволили мне чётко уяснить одну важную вещь: попытка скрытия сложных механизмов не приводит к простоте, она лишь ведёт к появлению ещё более сложных конструкций.
Вот, вкратце, основные идеи, которых я коснусь в этом материале:
- Программы на Java полны шаблонного кода, который скрывает намерения программиста.
- Работа со Spring и Spring Boot дала мне хороший урок, который заключается в том, что попытки скрыть сложные механизмы приводят к появлению ещё более сложных конструкций.
- Платформа Java EE была проектом, созданным, так сказать, «всеобщими усилиями», который покрывает абсолютно все нужды разработки корпоративных приложений. В результате платформа Java EE оказалась непомерно сложной.
- Разработка с использованием Spring — это, до определённого момента, занятие приятное. Эта иллюзия исчезает в тот день, когда из глубин некоей подсистемы, о которой вы никогда не слышали, всплывает исключение, которое совершенно невозможно понять, и на то, чтобы выяснить, в чём же заключается проблема, уходит не меньше трёх дней.
- Какие вспомогательные механизмы, создающие излишнюю нагрузку на систему, нужны фреймворку, который умеет «писать» код за программистов?
- Хотя IDE вроде Eclipse — это мощные приложения, они являются показателем сложности экосистемы Java.
- Платформа Node.js появилась в результате усилий одного человека, направленных на совершенствование его видения легковесной архитектуры, управляемой событиями.
- Кажется, что сообщество JavaScript с энтузиазмом воспринимает идеи избавления от шаблонного кода, что позволяет программистам максимально ясно выражать свои намерения.
- В качестве решения проблемы ада коллбэков в JS выступает конструкция async/await, которая является примером отказа от шаблонного кода и способствует ясности выражения намерений программистов.
- Программирование для Node.js — это сплошное удовольствие.
- В JavaScript нет строгой типизации, характерной для Java. Это — благословение и проклятие языка. Это позволяет легче писать код, но, для того, чтобы проверить его правильность, приходится уделять тестированию больше времени.
- Системой управления пакетами, представленной npm/yarn, легко и приятно пользоваться. Она не идёт ни в какое сравнение с Maven.
- И Java, и Node.js предлагают отличную производительность. Это идёт вразрез с мифом, в соответствии с которым JavaScript — это медленный язык, использование которого приводит к низкой производительности платформы Node.js.
- Производительность Node.js опирается на усилия Google по совершенствованию V8, движка, от которого зависит скорость работы браузера Chrome.
- Жестокая конкуренция между производителями браузерных JS-движков способствует развитию JavaScript, а это очень выгодно Node.js.
О проблемах разработки на Java
Некоторые инструменты или объекты являются результатом многолетних усилий инженеров по их совершенствованию. Программисты испытывают разные идеи, убирают ненужные атрибуты, и в результате у них получаются сущности, в которых имеется исключительно то, что нужно для решения некоей задачи. Часто таким технологиям присуще нечто вроде весьма привлекательной простоты, скрывающей мощные возможности. К Java это не относится.
Spring — это популярный фреймворк для разработки веб-приложений, основанных на Java.
Основная цель Spring, и, в частности, Spring Boot, заключается в предоставлении возможности пользоваться заранее настроенным стеком Java EE. Программист, который пользуется Spring, не должен, для того, чтобы создать готовую систему, заботиться о сервлетах, о системах постоянного хранения данных, о серверах приложений, и ещё неизвестно о чём. Все эти заботы перекладываются на плечи Spring, а программист занимается написанием кода, реализующего логику приложения. Например, механизмы JPARepository ответственны за генерирование запросов к базам данных для методов, названия которых выглядят как
findUserByFirstName
. Код таких методов программисту писать не приходится. Достаточно передать системе описание метода, а всё остальное сделает Spring.Звучит всё это очень хорошо, работать в таком стиле приятно, но — до тех пор, пока не случится какая-нибудь неожиданность.
Я имею в виду ситуацию, когда, например, приходит исключение Hibernate
PersistentObjectException
с сообщением detached entity passed to persist
. Что бы это могло значить? На то, чтобы это выяснить, ушло несколько дней. Как оказалось, если описать всё очень упрощённо, это значит, что JSON-данные, поступившие в конечную точку REST, имеют поля ID с некими значениями. Hibernate, опять же, если не вдаваться в детали, стремится контролировать значения ID, и, в результате, выбрасывает вышеописанное малопонятное исключение. Существуют тысячи таких вот сообщений об ошибках, сбивающих с толку и сложных для восприятия. Учитывая то, что в Spring имеются целые каскады подсистем, основанных друг на друге, стек Spring похож на заклятого врага программиста, который наблюдает за ним и ждёт, когда программист допустит малейшую ошибку, а когда это случается, бросается в него исключениями, не совместимыми с нормальной работой приложения.Далее, тут же можно вспомнить о длиннейших трассировках стека. Они представляют собой несколько экранов, полных всяких абстрактных методов. Spring, очевидно, создаёт конфигурацию, необходимую для реализации того, что выражено в коде. Подобный уровень абстракции, несомненно, требует немалого объёма вспомогательной логики, которая направлена на то, чтобы обнаружить всё необходимое для работы кода, например, для того, чтобы выполнять запросы. И длинные трассировки стека — это не обязательно плохо. Подобные вещи являются, скорее, симптомом, наводят на вопрос о том, какую нагрузку на систему создают вспомогательные механизмы.
Как выполняется метод
findUserByFirstName
, учитывая то, что программист не писал код подобного метода? Фреймворку нужно разобрать имя метода, понять намерение программиста, создать нечто вроде абстрактного синтаксического дерева, сгенерировать какой-то SQL-код, и так далее. Как всё это нагружает систему? И всё это существует лишь для того, чтобы программисту не нужно было бы писать код?После того, как вам придётся несколько десятков раз пройти через нечто вроде поиска смысла вышеописанной ошибки, тратя недели на то, чтобы разгадывать секреты, которые, по большому счёту, вы разгадывать не должны, вы можете прийти к тому же заключению, к которому пришёл я. Смысл его состоит в том, что попытка скрытия сложных механизмов не приводит к простоте, она лишь ведёт к появлению ещё более сложных конструкций. Платформа Node.js устроена гораздо проще.
Слоган «Compatibility Matters» скрывал в себе замечательную идею, в соответствии с которой важнейшей особенностью платформы Java была обратная совместимость. Мы относились к этому серьёзно, нанося на футболки изображения подобные тому, которое вы можете видеть ниже.
Обратная совместимость — это очень важно
Конечно, подобный уровень внимания к обратной совместимости может быть источником постоянной тревоги, и время от времени полезно уходить от старых механизмов, которые больше не приносят пользы.
Java и Node.js
Spring и Java EE чрезмерно усложнены. Платформа Node.js на их фоне воспринимается как глоток свежего воздуха. Первое, на что обращаешь внимание, знакомясь с Node.js — это подход Райана Даля к разработке ядра платформы. Его опыт подсказал ему, что платформы, использующие потоки, нужны для создания сложных, тяжеловесных систем. Он же искал чего-то другого, и потратил пару лет на совершенствование набора базовых механизмов, воплощённых в Node.js. В результате у него получилась легковесная система, которую характеризует один поток выполнения, изобретательное использование анонимных функций JavaScript в роли асинхронных коллбэков, и библиотека времени выполнения, оригинально реализующая асинхронные механизмы. Изначальным посылом при создании такой системы было обеспечение высокой производительности обработки событий с доставкой этих событий в функции обратного вызова.
Далее, важной особенностью Node.js является использование JavaScript. Возникает такое ощущение, что у тех, кто пишет на JS, имеется склонность к избавлению от шаблонного кода, что позволяет чётко описывать намерения программиста.
В качестве примера различия между Java и JavaScript рассмотрим реализацию функций-слушателей (наблюдателей). В Java для работы со слушателями нужно создать конкретный экземпляр абстрактного интерфейса. Это влечёт за собой использование громоздких языковых конструкций, которые скрывают суть происходящего. Как разглядеть намерение программиста, скрытое под покровами шаблонного кода?
В JavaScript вместо этого используются простые анонимные функции. Реализуя слушатель, не нужно искать подходящий абстрактный интерфейс. Достаточно, без необходимости пользоваться множеством вспомогательных текстов, написать необходимый код.
Итак, вот одна важная идея, которую можно вынести из анализа вышеописанных механизмов: большинство языков программирования скрывают намерения программиста, что приводит к тому, что код оказывается сложным для понимания.
Решение, касающееся использования функций обратного вызова, которое предлагает Node.js, выглядит весьма привлекательно. Но и оно не лишено проблем.
Решения проблем и проблемы решений
В JavaScript издавна существовали две проблемы, связанные с асинхронным программированием. Первая — это то, что в Node.js называется адом коллбэков. Заключается эта проблема в том, что, в ходе разработки, легко попасть в западню, построенную из глубоко вложенных функций обратного вызова, где каждый уровень вложенности усложняет программу, а также обработку результатов работы кода и ошибок. Существовала и ещё одна проблема, связанная с этой, суть которой в том, что языковые механизмы JavaScript не помогали программисту должным образом выражать идеи асинхронного выполнения кода.
Для упрощения асинхронной разработки на JS возникли несколько библиотек. Но это — очередной пример попытки скрытия сложных механизмов которая, ведёт лишь к появлению ещё более сложных конструкций.
Рассмотрим пример:
const async = require(‘async’);
const fs = require(‘fs’);
const cat = function(filez, fini) {
async.eachSeries(filez, function(filenm, next) {
fs.readFile(filenm, ‘utf8’, function(err, data) {
if (err) return next(err);
process.stdout.write(data, ‘utf8’, function(err) {
if (err) next(err);
else next();
});
});
},
function(err) {
if (err) fini(err);
else fini();
});
};
cat(process.argv.slice(2), function(err) {
if (err) console.error(err.stack);
});
Это — невзрачная имитация команды Unix
cat
. Библиотека async
отлично проявляет себя в деле упрощения последовательностей асинхронных вызовов. Однако её использование требует большого объёма шаблонного кода, скрывающего намерение программиста.В сущности, этот код содержит цикл. Он не написан как обычный цикл, здесь не используются естественные конструкции описания циклов. Далее, результаты выполнения кода и выдаваемые им ошибки не попадают туда, куда им правильно было бы попадать. Они оказываются запертыми в коллбэках, а это неудобно. Но, до появления в Node.js реализации стандартов ES2015/2016, ничего лучшего сделать было нельзя.
Если переписать этот код с учётом новых возможностей, которые, в частности, имеются в Node.js 10.x, то получится следующее:
const fs = require(‘fs’).promises;
async function cat(filenmz) {
for (var filenm of filenmz) {
let data = await fs.readFile(filenm, ‘utf8’);
await new Promise((resolve, reject) => {
process.stdout.write(data, ‘utf8’, (err) => {
if (err) reject(err);
else resolve();
});
});
}
}
cat(process.argv.slice(2)).catch(err => {
console.error(err.stack);
});
В этом примере мы воспользовались конструкцией
async/await
. Здесь представлены те же самые асинхронные механизмы, что и в предыдущем примере, но тут применяются обычные структуры, используемые при организации циклов. Работа с результатами и ошибками выглядит вполне обычным образом. Такой код легче читать и писать. Этот подход позволяет легко понять намерение программиста.Единственный недостаток заключается в том, что
process.stdout.write
не имеет Promise-интерфейса, в результате этот механизм нельзя использовать в async-функциях без оборачивания его в промис.Теперь можно сделать вывод о том, что проблема ада коллбэков в JavaScript была решена способом, который отличается от попытки скрытия сложных механизмов. Вместо этого внесены изменения в язык, что решило и саму проблему, и избавило нас от неудобств, вызванных необходимостью использовать большие объёмы шаблонного кода во временном решении. Кроме того, с применением механизма async/await код попросту стал красивее.
Этот раздел мы начинали с обсуждения недостатка Node.js, но отличное решение проблемы ада коллбэков привело к тому, что разговор о недостатках превратился в разговор о сильных сторонах Node.js и JavaScript.
Строгая типизация, интерфейсы и мнимая ясность кода
В те времена, когда я занимался защитой Java от разного рода нападок, я напирал на то, что строгая типизация позволяет писать огромные приложения. В те времена в ходу была разработка монолитных систем (не было микросервисов, не было Docker и тому подобных вещей). Так как Java является языком со строгим контролем типов, компилятор Java помогает программисту избегать множества проблем, не давая ему скомпилировать неправильный код.
JavaScript, в отличие от Java, не отличается строгой типизацией. Отсюда можно сделать очевидный вывод о том, что программист точно не знает, с какими именно объектами ему приходится работать. Откуда программисту узнать, что делать, например, с неким полученным откуда-нибудь объектом?
Обратная сторона строгой типизации Java — это необходимость в постоянном выполнении шаблонных действий. Программист постоянно производит приведение типов или занимается проверкой того, чтобы всё было в точности так, как ожидается. Разработчик тратит время на написание кода, делает это с исключительной точностью, использует немалые объёмы шаблонных конструкций, и надеется на то, что всё это поможет ему сэкономить время за счёт раннего обнаружения и исправления ошибок.
Проблема программирования на языке со строгой типизацией столь велика, что программист, практически без вариантов, должен использовать большую сложную IDE. Простого редактора кода тут недостаточно. Единственный способ поддержания Java-программиста в адекватном состоянии (за исключением пиццы) заключается в постоянном показе ему выпадающих списков, содержащих доступные поля объектов или описания параметров методов. Этот и другие вспомогательные механизмы таких IDE, как Eclipse, NetBeans, или IntelliJ, помогают в деле создания классов, облегчает рефакторинг и решение других задач.
И… не буду говорить о Maven. Это — просто кошмарный инструмент.
В JavaScript типы переменных не указывают при их объявлении, приведение типов обычно не используется, и так далее. В результате код легче читать, но такое положение дел означает и риск возникновения ошибок программирования, которые трудно обнаружить.
Относится ли вышеизложенное к плюсам Java или к минусам — зависит от точки зрения.
Десять лет назад я считал, что все эти сложности оправдывают себя тем, что дают программисту большую уверенность в коде, который он пишет. Сегодня я считаю, что строгая типизация увеличивает объём работы программиста и проекты гораздо легче разрабатывать так, как это делается в JavaScript.
Борьба с ошибками при помощи маленьких модулей, которые легко тестировать
Node.js подталкивает программиста к тому, чтобы он разбивал свои проекты на небольшие фрагменты, на так называемые модули. Возможно, этот факт покажется вам незначительным, но он частично решает только что упомянутую нами проблему.
Вот основные характеристики модуля:
- Самостоятельность. Модуль объединяет взаимосвязанный код в единую сущность.
- Чёткие границы. Код внутри модуля защищён от вмешательства в его работу каких-либо внешних механизмов.
- Явный экспорт. По умолчанию код и данные модуля не экспортируются. Разработчик самостоятельно решает, какие функции и данные нужно сделать общедоступными.
- Явный импорт. Программист, при разработке модуля, сам решает, от каких модулей он будет зависеть.
- Потенциальная независимость. Модули можно сделать общедоступными, в весьма широком смысле этого слова, публикуя их в npm, или, если они предназначены для внутренних нужд компании, публикуя в закрытых репозиториях. Это позволяет легко использовать одни и те же модули в разных приложениях.
- Простота понимания кода. То, что модули имеют небольшие размеры, упрощает чтение и понимание их кода, открывает возможность для свободных дискуссий о них.
- Облегчение тестирования. Маленький модуль, если он реализован правильно, легко поддаётся модульному тестированию.
Всё это делает Node.js-модули сущностями с чётко очерченными границами, код которых легко писать, читать и тестировать.
Однако беспокойство при работе с JavaScript вызывает тот факт, что отсутствие строгой типизации может легко привести к тому, что код сделает что-то не то. В маленьком модуле, нацеленном на решение какой-то узкой задачи, обладающем чёткими границами, «что-то не то» может затронуть лишь код самого модуля. Это приводит к тому, что проблемы, которые может вызвать отсутствие строгой типизации, оказываются запертыми в границах модуля.
Ещё одно решение проблемы динамической типизации в JavaScript заключается в тщательном тестировании кода.
Разработчику приходится серьёзно подходить к тестированию, что отнимает у него часть выгод, которые исходят из простоты процесса разработки на JS. Системы тестирования, создаваемые JS-программистом, должны находить те ошибки, которые, разрабатывай он на чём-то вроде Java, мог бы автоматически находить компилятор. Вы ведь пишете тесты для своих JS-приложений?
Тем, кому нужна система статической типизации в JavaScript, может быть полезно взглянуть на TypeScript. Я не пользуюсь этим языком, но мне доводилось слышать о нём много хорошего. Он совместим с JavaScript и расширяет язык системой контроля типов и другими полезными возможностями.
В итоге можно сказать, что использование модульного подхода к разработке — это сильная сторона Node.js и JavaScript.
Управление пакетами
Мне плохо от одной мысли о Maven, поэтому я не могу даже нормально писать о нём. И, насколько я понимаю, Maven, без компромиссов, либо любят, либо ненавидят.
Проблема тут заключается в том, что в среде Java нет целостной системы для управления пакетами. Пакеты Maven существуют, с ними можно нормально работать, их поддерживает Gradle. Но то, как организована работа с ними, и близко не похоже на те удобства, которые даёт разработчику система управления пакетами для Node.js.
В мире Node.js существуют два отличных менеджера пакетов, которые работают в тесной связи друг с другом. Сначала единственным подобным инструментом был репозиторий npm и одноимённый инструмент командной строки.
Благодаря npm в нашем распоряжении имеется отличная схема для описания зависимостей пакетов. Зависимости могут быть строгими (скажем, указывается, что нужна исключительно версия 1.2.3 некоего пакета), или заданными с несколькими степенями свободы — вплоть до
*
, что означает использование самой свежей версии некоего пакета. Сообщество Node.js опубликовало в репозитории npm сотни тысяч пакетов. При этом использовать пакеты, которых нет в npm, так же легко, как и пакеты из npm.
Система npm получилась настолько удачной, что пользуются ей не только разработчики серверных продуктов на Node.js, но и программисты, занимающиеся фронтендом. Раньше там, для управления пакетами, использовались инструменты вроде Bower. Bower был признан устаревшим, и теперь можно обнаружить, что все JS-библиотеки для разработки фронтенда существуют в виде npm-пакетов. Многие вспомогательные инструменты для клиентской разработки, вроде Vue.js CLI и Webpack, написаны в виде Node.js-приложений.
Ещё одна система управления пакетами для Node.js, yarn, загружает пакеты из репозитория npm и использует такие же конфигурационные файлы. Основное преимущество yarn перед менеджером пакетов npm заключается в более высокой скорости работы.
Репозиторий npm, независимо от того, работают ли с ним с помощью менеджера пакетов npm или c помощью менеджера пакетов yarn, представляет собой мощную основу того, что делает разработку для Node.js такой простой и приятной.
Однажды, после того, как я помогал в разработке java.awt.Robot, я вдохновился на создание этой вот штуки. В то время как официальное изображение Duke состоит из кривых, RoboDuke построен из прямых линий. Только локтевые суставы у этого робота круглые
Производительность
И Java, и JavaScript критиковали за их невысокую производительность. В обоих случаях компилятор преобразует исходный код программы в байт-код, выполняемый на виртуальной машине, реализованной для конкретной платформы. Виртуальная машина, в свою очередь, преобразует байт-код в машинный код с использованием различных оптимизаций.
И у Java, и у JavaScript есть причины стремиться к высокой производительности. Если говорить о Java и о Node.js, то их роднит стремление к быстрому серверному коду. В случае с браузерным JavaScript стимулом к высокой производительности является повышение качества клиентских приложений. Мы поговорим об этом в разделе о насыщенных интернет-приложениях.
JDK Sun/Oracle использует HotSpot — виртуальную машину, поддерживающую множество стратегий компиляции байт-кода. Название этой виртуальной машины намекает на технику оптимизации, в ходе реализации которой обнаруживается код, который выполняется чаще всего, и к такому коду, чем более интенсивно он используется, применяется всё больше оптимизаций. HotSpot — это высокооптимизированная система, которая производит очень быстрый код.
Если говорить о JavaScript, то раньше мы задавались вопросом о том, как можно ожидать от JS-кода, выполняемого в браузере, возможностей, необходимых для реализации каких-либо сложных приложений. Считалось, что на браузерном JavaScript практически невозможно было бы создать нечто вроде набора традиционных офисных приложений. Сегодня для доказательства того, что это возможно, далеко ходить не надо. Я, например, пишу этот материал в Google Docs, и производительность меня полностью устраивает. Скорость работы браузерного JS улучшается с каждым годом.
Node.js следует в том же направлении, так как он использует движок V8 браузера Google Chrome.
В качестве примера можно привести это выступление Питера Маршалла, инженера Google, который занимается работой над V8, и основной задачей которого является улучшение производительности этого движка. Здесь он рассказывает о том, почему V8 перешёл с Crankshaft на Turbofan.
Машинное обучение — это область, в которой используются тяжёлые вычисления, для выполнения которых обычно пользуются языками R или Python. Машинное обучение, как и некоторые другие области, нуждаются в средствах для быстрого выполнения численных вычислений. Здесь JavaScript, по разным причинам, не особенно силён, но сейчас ведётся работа по созданию стандартизированной библиотеки для организации численных вычислений на JavaScript.
Из этого видео можно узнать об использовании в JavaScript новой библиотеки, TensorFlow.js. API этой библиотеки похоже на API TensorFlow для Python, она поддерживает импорт предварительно обученных моделей. Эту библиотеку можно использовать, например, для анализа видео с целью распознавания объектов, при этом все необходимые вычисления выполняются в браузере.
Вот выступление Криса Бэйли из IBM, где он затрагивает вопросы производительности и масштабируемости Node.js, в частности, при использовании конфигураций, основанных на Docker/Kubernetes. Он начинает рассказ с рассмотрения набора бенчмарков, которые демонстрируют значительно более высокую производительность Node.js в сравнении со Spring Boot. Речь идёт о пропускной способности подсистемы ввода-вывода, о времени запуска приложения и о потреблении памяти. Более того, от релиза к релизу производительность Node.js серьёзно улучшается, отчасти, благодаря улучшениям, вносимым в V8.
Здесь Бэйли говорит, что Node.js не подходит для выполнения интенсивных вычислений. Нам важно понять причину подобной рекомендации. Из-за того, что в Node.js используется однопоточная модель выполнения кода, длительные вычисления блокируют обработку событий. Я, в моей книге «Node.js Web Development», затрагиваю эту проблему, приводя три подхода к её решению:
- Рефакторинг алгоритмов — выявление медленных частей алгоритма и его рефакторинг для достижения более высокой скорости работы.
- Разбиение процесса вычислений на небольшие фрагменты с использованием диспетчера событий, что позволяет Node.js регулярно возвращаться к главному потоку.
- Передача тяжёлых вычислительных задач на вспомогательный сервер.
Если производительности JavaScript для ваших задач недостаточно, взгляните на следующие два способа интеграции нативного кода в Node.js. Самый простой способ — это использование нативных Node.js-модулей. В наборе вспомогательных инструментов для Node.js-разработки имеется средство
node-gyp
, которое помогает работать с такими модулями. Вот видео, в котором демонстрируется интеграция Rust-библиотеки с Node.js.WebAssembly позволяет компилировать программы, написанные на разных языках, в подмножество JavaScript, отличающееся очень высокой скоростью выполнения. WebAssembly представляет код, который выполняется внутри JavaScript-движка. В этом видео дан хороший обзор технологии и показано использование WebAssembly в среде Node.js.
Насыщенные интернет-приложения
Насыщенные интернет-приложения (Rich Internet Applications, RIA) были у всех на слуху лет десять назад. Тогда говорили о том, что они, реализованные на базе быстрых (для своего времени) JS-движков, способны сделать неактуальными традиционные настольные приложения.
На самом деле, эта история началась более 20 лет назад. Sun и Netscape договорились об использовании Java-апплетов в Netscape Navigator. JavaScript был, отчасти, разработан как скриптовый язык для Java-апплетов. Тогда индустрия надеялась на то, что на серверах будут использоваться Java-сервлеты, на клиентах — Java-апплеты. В результате разработчики попадут в замечательную ситуацию, когда и для серверов и для клиентов можно будет писать на одном и том же языке. Этого не случилось по разным причинам.
Десять лет назад JavaScript оказался достаточно мощным для того, чтобы сложные приложения можно было бы реализовывать исключительно его средствами. В результате появился модный тогда термин RIA, и ожидалось, что насыщенные интернет-приложения убьют Java в виде платформы для клиентских веб-приложений.
Сегодня мы начинаем видеть, как идея RIA начала приносить плоды. Благодаря Node.js стала реальной та желанная для многих ситуация, когда, и на сервере и на клиенте, используется один и тот же язык. Только теперь это JavaScript.
Вот несколько примеров:
- Набор приложений Google Docs (использованный при написании этой статьи), который очень похож на типичный комплект офисных приложений, но работающий в браузере.
- Мощные фреймворки, вроде React, Angular и Vue.js, упрощают разработку браузерных приложений, основанных на HTML, CSS и JavaScript.
- Electron — это смесь Node.js и браузера Chromium. Данный фреймворк предназначен для разработки кросс-платформенных настольных приложений. С его использованием созданы такие весьма популярные и качественные приложения, как Visual Studio Code, Atom, GitKraken, и Postman.
- Так как в Electron/NW.js применяется браузерный движок, веб-фреймворки, такие, как React, Angular, и Vue, можно использовать для разработки настольных приложений.
Технология Java, в роли платформы для разработки настольных приложений, умерла не из-за насыщенных интернет-приложений, написанных на JavaScript. Она умерла, преимущественно, из-за невнимания к клиентским технологиям в Sun Microsystems. В центре внимания Sun были корпоративные пользователи, которым нужна высокая производительность серверных приложений. Я видел всё это своими глазами. Настоящим убийцей Java-апплетов стала проблема в безопасности, выявленная несколько лет назад в плагине Java и в Java Web Start. Это привело ко всемирному сокращению использования Java-апплетов и Webstart-приложений.
Другие виды настольных приложений всё ещё можно разрабатывать на Java, и, как результат, IDE NetBeans и Eclipse всё ещё борются друг с другом. Однако в этой сфере применения Java наблюдается застой, и за пределами инструментов разработки существует очень немного приложений, основанных на Java.
Исключением является технология JavaFX.
Технология JavaFX, 10 лет назад, планировалась как ответ Sun на появление iPhone. Планировалось, что эта технология позволит разрабатывать на платформе Java, доступной на мобильных устройствах, приложения, обладающие насыщенным графическим интерфейсом. Это позволило бы одним махом вывести из игры Flash и средства разработки приложений для iOS. Но ничего такого не произошло. JavaFX используется до сих пор, но эта технология не видела столь масштабного взлёта, на который рассчитывали её создатели. В наши дни всеобщее внимание притягивают веб-фреймворки вроде React, Vue.js и им подобных.
В описанной ситуации JavaScript и Node серьёзно обогнали Java.
Вот фотография кольца Java, которое как-то раздавали на конференции JavaONE. Такие кольца содержали чип с полной реализацией Java на борту. Их, в основном, использовали для разблокировки компьютеров, установленных в вестибюле.
Кольцо Java
Инструкция к кольцу
Итоги
В наши дни разработчикам серверных проектом есть из чего выбирать. Индустрия больше не ограничена так называемыми «P-языками» (Perl, PHP, Python) и Java, так как теперь есть платформа Node.js, есть языки Ruby, Haskell, Go, Rust, и многие другие. В результате у серверных программистов теперь есть просто огромный выбор замечательных технологий.
Если же говорить о том, почему я, человек, который жил исключительно Java, перешёл на сторону Node.js, то ясно, что меня привлекла свобода, которая характерна для Node.js-разработки. Экосистема Java превратилась в обузу, а при использовании Node.js ничего такого не ощущается. Конечно, если мне, по работе, придётся писать на Java, я выполню эту задачу.
У каждого приложения имеются собственные нужды. И, безусловно, неправильно всегда и для всего использовать исключительно Node.js только из-за того, что эта платформа кому-то нравится. Выбор того или иного языка или фреймворка должен определяться техническими соображениями. Например, недавно мне пришлось работать с XBRL-документами. Так как лучшие библиотеки для работы с XBRL написаны на Python, для того, чтобы ими пользоваться, надо знать Python. Поэтому, выбирая технологии, нужно здраво оценивать реальные задачи проектов и останавливаться именно на том, что лучше всего подходит для решения этих задач.
Уважаемые читатели! Если вы, как и автор этой статьи, перешли на JavaScript с какого-то другого языка, или сменили какую-нибудь серверную платформу на Node.js, просим в двух словах об этом рассказать.