Данная статья является продолжением предыдущей, которая вызвала много хороших и интересных вопросов. Когда я начал писать ответы на комментарии, то понял, что о многом не рассказал. Поэтому решился написать новую полноценную статью, но уже под другим углом: добавил личный опыт и причинно следственные связи. Если вы не читали предыдущую, то ничего страшного. Можете и не читать.

Основная мысль — в середине 2017 года я инициировал смену основного стека разработки бэкенд приложений, разрабатываемых нашей компанией с .Net (C#) на Node.js (typescript). Как это вышло, жалею ли о принятом решении и не собираюсь ли назад? Об этом ниже.

Предыстория


Сначала небольшой рассказ о себе. Родился я программистом, не менеджером. Свое первое приложение, о котором не стыдно рассказать, сделал в 2003 году на С++ в IDE Visual Studio 6 будучи студентом. Это была игра «Трансформеры» с применением D3D, DirectSound и почти всех других модулей, входивших на тот момент в состав DirectX 9, не ну а чё, «я ж программист».



Мы с одногруппником писали эту игру около года. Это было именно то программирование, о котором я мечтал с детства — очень высоко-технологичное, с применением инкапсуляции, полиморфизма и других прелестей ООП, а также хитрых алгоритмов и необычных структур данных, типа фибоначчиевых куч.

Тогда казалось, что для студентов 3 курса без опыта работы и внятного портфолио это была единственная возможность устроиться на работу в очень популярную в то время GameDev компанию. Но у нас ничего не вышло. После 4х часового собеседования, на котором, среди прочего, мы писали на бумаге алгоритм пересечения двух прямых на assembler нам отказали, сославшись на то, что не готовы сейчас связываться со студентами.

C# .Net


Спустя пару месяцев после этой неудачи я оказался в большой (более 100 программистов) софтверной компании, где с появлением C#, перешел на него.



В результате общения с более опытными и высококвалифицированными коллегами, я на практике овладел такими интересными штуками, как:

  • аспектно-ориентированное программирование (АОП) для устранения служебного кода из бизнес логики;
  • кодогенерация на tt-шаблонах, для уменьшения рутины по созданию однотипного кода;
  • NServiceBus, MSMQ, WCF для межсервисного взаимодействия;
  • Dependency Injection (DI);
  • принцип построения интерфейсов с учетом Data Load Options (DLO) для указания связанных объектов, информация о которых также должна быть получена в результате выполнения запроса. Что-то типа самописного GraphQL, которого на тот момент не существовало в публичном виде;
  • профилирование и оптимизация SQL запросов;
  • создание веб-приложений на ASP.Net MVC, knockout и angular.js.

Основным же минусом было катастрофически малое количество интересных задач, где можно было бы применить накопленный багаж знаний алгоритмов и структур данных, а также всевозможных паттернов. Ну я же программист, я не могу без этого!



Подразделение, в котором я работал, разрабатывало интерфейсы, по которым данные из СУБД становились доступными другим системам. Год за годом в голове стала крепнуть мысль, что в бездушном enterprise все именно так и что ООП для задач типа «возьми вот это, немного покрути и отдай вот туда» так себе идея.

На уровне реляционной СУБД и контрактов данных в REST API ООП поддерживается только энтузиазмом разработчика, который хочет писать на ООП и только на нем. А логика промежуточного слоя (между взять из базы и отдать потребителю) по сложности и связанности — это ни разу не GameDev и может быть легко написана в функциональном стиле.

В середине 2015 года я с 8-ми летним опытом работы на C# отправился за интересными задачами в компанию SoftMediaLab.



Благодаря накопленному опыту как на бэкенде, так и на фронте я сразу стал руководителем разработки нового достаточно крупного проекта — online-площадки p2p и b2p кредитования. На бэкенде был C# .Net Framework 4.6, микросервисная архитектура, общение микросервисов по WCF, postgreSQL 9.5, ORMLite, кодогенерация ORM-классов для хисторных таблиц и скриптов раскатки/перенакатки объектов СУБД, в СУБД около 150 таблиц, postsharp 2.1 для АОП, сокеты на SignalR, посадочные страницы и WEB API на основе ASP.Net MVC. На фронте angular.js и JS т.к. typescript в то время еще не имел широкого распространения. CI/CD, потоковая репликация на дублирующий узел.

После завершения проекта в середине 2016 года мы совместно с основными разработчиками провели ретроспективу. На ней обозначили следующие проблемы, они же перспективы для развития:

  • в фронтенд части любой рефакторинг может легко привести к ошибкам по невнимательности из-за отсутствия статической типизации JavaScript и дороговизне полноценных тестов на UI;
  • в системе много логики, которую пришлось писать и поддерживать в двух местах — на C# в бэкенде и на фронте в JS. Эта логика была связана с работой кредитного калькулятора: расчеты плана платежей, пролонгации, досрочного закрытия договора и т.д. По требованиям этот калькулятор также должен был присутствовать на WEB-сайте и работать без обращений в бэкенд;
  • часто возникала необходимость провести небольшую по сложности доработку, затрагивающую бэк и фронт одновременно, которую один фулстек делал за такое же время, как и два программиста — отдельный бэкендер и отдельный фронтендер. При этом, в случае с одним фулстеком, разработку и тестирование таких задач было существенно проще планировать в спринтах;
  • в системе много кода, связанного с преобразованиями моделей данных в/из DTO. Лучше было бы этот код писать и поддерживать в актуальном состоянии не руками, а кодогенерацией;
  • найти хорошего фулстек программиста на стеке (C#, angular или react) для усиления команды сложно.

После этого проекта был следующий, который мы писали также на C#. Но чтобы закрыть проблему рефакторинга JavaScript кода на фронте мы единогласно приняли решение перейти с angular.js на angular.io с поддержкой typescript из коробки.

У меня к Typescript возникла любовь с первого взгляда.



Конечно, это был не такой строго типизированный C#, а скорее намек на типизацию, с помощью которой программист описывал свои ожидания от структуры объекта с которым работает. А наши ожидания, это… ну вы помните…



Но все таки, это был настоящий прорыв после чистого JavaScript и рефакторинг фронтенд части на этом проекте уже перестал быть ощутимой проблемой. А значит поставленная цель достигнута!

Node.js


В начале 2017 года, после того, как typescript показал себя с лучшей стороны на фронте, идея попробовать писать бэкенд на typescript показалась мне логичной.

На тот момент к разработке бэкенда у меня были следующие требования:

  • код для раскатки/перераскатки СУБД, включая триггерные функции и хисторные таблицы должен быть создан автоматически — кодогенерацией или ORM;
  • код создания моделей DTO и мапперов в/из них должен быть создан автоматически с помощью кодогенерации;
  • swagger-описание REST API должно автоматически генерироваться из исходного кода;
  • должна существовать полноценная ORM-система, позволяющая работать с MS SQL, PostgreSQL, mongoDb, а также писать кастомные запросы для особых случаев;
  • должны существовать библиотеки интеграции с redis, rabbitMQ, socket, т.к. вероятность использования данных инструментов на новых проектах очень высока;
  • кросплатформенность — чтобы у разработчиков был выбор ОС;
  • стабильность и отказоустойчивость;
  • возможность оптимизации узких по производительности мест на проверенной и хорошо зарекомендовавшей себя технологии;
  • удобная IDE, которая позволяет легко писать, рефакторить и отлаживать код.

Исследования пунктов, не связанных с кодогенерацией, я провел в фоновом режиме в течении месяца и нашел такие стоящие решения как typeorm, express.js, socket.io, redis, typescript-rest-swagger.

В середине 2017 года node.js уже была достаточно стабильной технологией. За пару часов в Google я нашел с десяток сервисов, работающих в условиях высокой реал-тайм нагрузки, реализованных на ноде. Также узнал, что оптимизацию узких по производительности мест нужно выполнять на C++.

Для исследования возможностей кодогенерации я принял на работу толкового выпускника технического института с небольшим опытом работы на C# и вообще без опыта работы на JS/TypeScript. Чтобы, среди прочего, проверить порог вхождения в TypeScript.

Месячным результатом работы толкового выпускника стало три библиотеки — grunt-generate-view-model, grunt-generate-history-model, grunt-generate-database, которыми наша компания пользуется на проектах до сих пор. Только префикс «grunt-» сейчас уже не актуален. Он тянется из исходных версий, когда они работали еще и как плагины сборщика проектов grunt.

В конце лета 2017 года я собрал совещание технических специалистов, на котором презентовал возможности node.js, и, большинством голосов, мы приняли решение сделать следующий коммерческий проект в связке node.js (TS) + angular.io (TS).

Так получилось, что в это время в нашей компании одновременно началось два проекта:

  • коммуникационная платформа, позволяющая сотрудникам офиса общаться с клиентами по Telegram, WhatsApp, FB, VK и Viber в одном окне;
  • и проект по автоматизации внутренних бизнес процессов одного из наших клиентов.

Оба проекта были успешно реализованы на новом стеке. С тех пор, все новые проекты мы делаем на TS. Бэкенд на node.js, фронт на react (почему перешли с angular.io на react — тема для отдельного материала), а мобильные приложения на react-native.

Ретроспектива три года спустя


С момента внедрения node.js наша компания реализовала более 10 проектов на данном стеке.



Какие плюсы получили от перехода с .Net C# на node.js typescript?

Разработчики:

  • легко и с удовольствием увеличивают свои компетенции в смежных областях. Например, к нам в компанию в начале этого года пришел верстальщик, который в течении испытательного срока успел поучаствовать в проекте, как бэкенд разработчик и как разработчик мобильных приложений. С учетом того, что у нас на проектах обязательно CI, CD и code review, я доволен результатом его работы (CI проходит, кол-во замечаний по code review уменьшается от пул реквеста к пул реквесту);
  • получают удовлетворение от быстрого перезапуска обновленной версии в режиме разработки с помощью инструмента nodemon. Достаточно сохранить изменение в файле, как утилита мгновенно на это отреагирует, соберет и запустит обновленную версию бэкенда;
  • пишут максимально переиспользуемый код (один на бэк, web и mobile);
  • работают в привычной для себя ОС и IDE;
  • имеют больше возможностей для обмена опытом и собственном развитии т.к. вокруг все фулстеки, но кто-то круче в одном, а кто то в другом;

Тимлиды:

  • не разруливают споры между бэкендерами и фронтендерами;
  • планируют небольшие доработки по функционалу, затрагивающему бэк и фронт из расчета на одного фулстек-разработчика;
  • выбирают исполнителя для задачи в зависимости от индивидуальных особенностей разработчиков безотносительно того бэкендер это или фронтендер. Например, более аккуратный и дотошный разработчик чаще верстает pixel-perfect, а программист с хорошо развитым алгоритмическим мышлением чаще пишет логику на фронте и на бэке;

HR:

  • рассматривают большой круг кандидатов: не важно, верстаешь ты на react, пишешь на angular или node.js, а может быть ты программист react-native? Мы рады всем, особенно кто имеет опыт и понимание необходимости использования typescript в проектах;

CTO:

  • имеет большой выбор при составлении команды под новый проект;
  • может ускорить разработку за счет подключения новых разработчиков;
  • не ломает голову, на кого заменить тимлида или сотрудника на проекте, когда тот уходит в отпуск;
  • укладывается в обозначенный на старте проекта срок и трудозатраты в 90% случаев.

Но есть и объективные минусы, куда же без них:

  1. один отличный и еще один неплохой разработчик, которые не захотели переходить на node.js, больше у нас не работают;
  2. отладка кода на Typescript с помощью breakpoint из под VS Code в связке с nodemon полный отстой. Точки остановки то не срабатывают, то срабатывают, но не там. В итоге это приводит к тому, что рано или поздно разработчик сдается и начинает отлаживать код вставками console.log;
  3. Typescript — ваши ожидания, это ваши проблемы. Часто новички попадают впросак, когда, например, объявляют целочисленную переменную в которую получают значение из внешней библиотеки или, скажем, инпута с формы и долго недоумевают, как же так получилось, что у них в переменной оказалось string-значение;
  4. если при компиляции ts в js появляются кольцевые зависимости (например, файл a.ts импортирует b.ts, а b.ts импортирует a.ts), то ошибка возникает на этапе выполнения (значения создаваемых инстансов импортируемых классов undefined), а не на этапе компиляции;
  5. инфраструктура npm иногда (по нашему опыту 1-2 раза в год) преподносит неприятные сюрпризы. Когда обновление версии какой-либо библиотеки подключенной через ^ в зависимой библиотеке (зависимость зависимости) выводит из строя всё приложение. Для минимизации таких проблем создан служебный файл с описанием всего дерева зависимостей package-lock.json. Но в случаях, когда разработка ведется на разных ОС случаются ситуации, когда этот механизм не работает без кропотливого ручного вмешательства.

Холиварные минусы node.js и окружения:

  1. нужна высокая квалификация, чтобы писать расширения на C++. Но так получилось, что за три года у нас ни разу не возникла в этом необходимость. В производительность JS-слоя не упирались;
  2. возможности для реализации ООП и рефлексии гораздо беднее, чем на C#. Но опять таки, функциональное программирование с использованием промежуточных слоев обработки (middleware) — это хорошая альтернатива для очень широкого круга задач;
  3. максимально облегченная VS Code по сравнению с мощной VS;
  4. вместо мощного LINQ встроенные JS-методы по работе с массивами, такие как filter, map, reduce, sort;
  5. в полноценной VS лучше средства профилирования приложения под нагрузкой.

Итого


Я не жалею о том, что принял решение перевести компанию на node.js. Плюсы на наших проектах (а это автоматизация внутренних бизнес процессов любой сложности, тендерные площадки, p2p/b2b/b2p платформы, CRM-системы, социальные WEB-сервисы, MVP любых бизнес идей) на мой взгляд перевешивают минусы. Сроки по проектам не срываются. Набор новых сотрудников активно идет, а у прошедших испытательный срок, глаза горят, а руки рвутся в бой!



При этом конечно же есть сферы, где node.js сейчас не катит. Это GameDev, Data Science, Machine Learning, AI. Но оно и понятно. Если бы был идеальный инструмент для всего, то других бы не осталось.