В первой части вы узнали, что по веским причинам мы были вынуждены выбрать новый технический стек для дальнейшего развития нашего продукта. Пора перейти к самому интересному: что же мы будем использовать вместо Dart? 

Язык

Основными критериями, которыми мы руководствуемся при выборе языка, являются, как и пять лет назад:

  • Строгая типизация, которая помогает большим (и даже огромным) командам более эффективно работать над единой кодовой базой.

  • Развитие и поддержка сообщества.

  • Наличие готовых решений, если не в виде библиотек, то хотя бы в виде паттернов, best practices и описанных подходов. Мы бы не хотели переизобретать велосипед.

  • Наличие систем сборки, которые будут работать с большими объёмами. Как показала практика, не всё, что хорошо работает для трёх-пяти разработчиков, хорошо скейлится до 100+.

  • Понятный большинству синтаксис, который позволит быстро технически онбордить новых разработчиков.

И, если анализировать рынок именно языков, то получится, что выбор-то и не такой большой. Dart, TypeScript, JavaScript с различными линтерами и всё. Более «маргинальным» выбором могут быть Clojure, Reason или даже Kotlin, но почти все они не отвечают какому-то одному из наших требований. А это значит, что в качестве языка разработки, кроме Dart, мы можем выбрать только TypeScript.

Какой из фреймворков выбрать?

Фреймворк

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

Основные критерии, которые мы предъявляли к фреймворку, похожи на те, что мы использовали для выбора языка:

  • Поддержка сообщества.

  • Минимальные изменения в текущей инфраструктуре и подходах, чтобы цена переезда не превысила пользу от переезда.

  • Перформанс в большом приложении. Причём не только в рендеринге, но и в скорости разработки.

Рассмотрим подробнее критерии, которые мы предъявляли к фреймворку, с примерами из Angular и React. Именно их мы брали в качестве основных претендентов на использование.

Организация кода и сборка

Под организацией кода подразумевается разделение кода по репозиториям и пакетам. 

Так как за эти годы мы написали хорошо настроенную систему сборки, да и само приложение часто не «переваривается» стандартными средствами, от фреймворка нам нужна была максимальная гибкость и производительность. Код на Dart мы организуем в мульти-репозиторий, в котором сейчас уже больше 350 библиотек. Именно на такой подход настроены все наши инструменты сборки, и переход на mono-repo может стоить достаточно дорого.

NB: В Dart мы используем пакетный менеджер pub с публикацией исходного кода пакетов. Это позволяет очевидным образом использовать мульти-репозиторий и уметь изменять сразу несколько пакетов: достаточно прописать оверрайды на git-ветку и получить доступ к исходникам.

Один из главнейших плюсов React для нас здесь в том, что ему ничего не нужно знать о твоем flow разработки, фреймворк можно настроить более-менее как угодно.

Angular, в свою очередь, больше похож на enterprise систему — много что дает из коробки (angular cli), но конфигурируется это плохо — надо либо брать Angular flow, или не брать этот фреймворк вовсе.

React позволяет нам остаться на мульти-репозитории и получить DevExp + CI/CD, аналогичные текущим. Для Angular в его текущем состоянии реалистичным выглядит только моно-репозиторий. 

Data flow

Под data flow подразумевается модель организации (потока) данных в приложении. Это определяет абстракции, с которым разработчик будет сталкиваться каждый день.

В React и Angular в обоих случаях ключевой абстракцией является компонент, у которого есть свои входные параметры (props/inputs) и собственное состояние. Перерисовка идет сверху вниз по дереву компонентов. Основное отличие — в React перерисовка идет от узла с новым состоянием вниз, в Angular — от рута до компонентов, для которых (явно или нет) вызван markForCheck, а затем вниз.

Из-за того, что механизм change detection в Angular никуда не девается и выполняется от рутового компонента, перерисовка изменений в отдельных компонентах выполняется не так быстро, как в React. Это закрывает целый ряд возможностей, которые предоставляет React — такие как controlled <input>.

В React нет и никогда не было зон — глобального контекста, который влияет на работу всего приложения. Это приводит к тому, что требуется меньше времени на отладку и профилирование приложения, т.к. проблемы «локальны» для определенного поддерева компонентов. Тем не менее, Angular для TS позволяет отказываться от использования zone.js, но эта возможность до сих пор считается экспериментальной.

Композиция компонентов

Под композицией компонентов понимается возможность передать один компонент в другой. Такие компоненты часто называют «шаблонизируемыми».

React значительно обходит Angular по возможностям композиции компонентов. Для React отсутствует такие понятия как статичный шаблон или динамическая подгрузка компонентов. Всё по умолчанию «динамичное»: есть возможность передавать одни компоненты в другие, выполнять обработку отрендеренного шаблона кодом. Это позволяет создавать более гибкие компоненты, не зашивать все визуальные элементы и логику внутри одного компонента, разделять компоненты на UI-only и компоненты с логикой, писать и тестировать их в изоляции.

Итог

Если подвести итог, Angular даёт достаточно большие возможности «из коробки», но при этом встраивание его в существующую экосистему будет равнозначно переписыванию её с нуля. Это выглядит нецелесообразным. React же достаточно гибок и позволяет переиспользовать элементы нашей инфраструктуры.

Таким образом, на сегодняшний момент мы приняли решение выбрать связку Typescript + React в качестве основных инструментов разработки клиентской части нашего приложения.

План миграции

Language agnosticism

Множество актуальных крупных фич останутся написанными на Dart еще долгое время, поэтому Dart для нас не превращается в Legacy. Бессмысленно переписывать то, что разрабатывалось годами и нормально работает.

Но чтобы разработка была эффективной и консистентной на обоих стеках: Dart и TS, мы начинаем действовать по двум направлениям: 

  1. Вовлечение и повышение экспертизы у разработчиков компании. Если инженеры приступят к переходу на TS в начале работы над первой продуктовой разработкой, то от этого пострадает качество и сроки реализуемой фичи. Чтобы этого не произошло, наращивание экспертизы необходимо провести до начала разработки большой фичи.

  2. Развитие и поддержка однотипного инструментария для обоих стеков. Переключение между стеками разработки в рамках одного приложения приводит к тому, что разработчики вынуждены терять время на адаптацию. Чтобы времени терялось меньше, необходимо обеспечить использование инструментов (библиотеки, парадигмы, архитектурный подход и т.п.) максимально похожими на Dart / TS.

Более детальный план по внедрению работы на TS для нас выглядит следующим образом:

  1. Обеспечить схожий Dev Experience на обоих стеках. Правила линтинга, архитектурный подход для Dart/Angular и TS/React, unit-тестирование, локализация.

  2. Обеспечить единый процесс деплоя для приложения на разных стеках. Процесс доставки приложения до production единый и автоматизированный вне зависимости от стека разработки

  3. Обеспечить консистентный набор библиотек для разработки. Те кейсы, которые закрывались на Dart-стеке с помощью библиотек, так же закрываются на TS-стеке аналогами таких же библиотек. Например, работа с сессиями пользователей, маршрутизация, разные виды транспорта, генерация контрактов и т.п. При этом развитие таких библиотек гарантирует feature parity между реализациями на Dart и TS.

  4. Обеспечить консистентность интерфейса. На одной странице могут оказаться два приложения на разных стеках. Интерфейс обоих приложений должен состоять из одинаковых компонент. Для этого гарантируется поддержка и развитие визуальных компонент с соблюдением feature parity на обоих стеках.

  5. Проведение внутреннего обучения для разработчиков. Проведение воркшопов, вовлечение в новые проекты на TS, которые не имеют высокого продуктового приоритета, с целью наращивания экспертизы в спокойной обстановке.

Помимо этого, мы движемся в сторону micro-frontends. Но это большой и сложный вопрос, который можно обсудить в наших будущих статьях.

Острые вопросы

Q: Будете ли вы писать на Dart дальше?

A: Поддержка и развитие существующих фич на Dart продолжится. А также вполне возможно, что некоторые новые фичи тоже будут написаны на Dart, например, те, для которых уже существует большое количество готовых модулей.

Q: Будете ли вы требовать от новых разработчиков знания Dart?

A: Мы не делали этого и раньше. Наш подход состоял в обучении и онбординге новых инженеров, так как, стоит признать, Dart-разработчики весьма редки на рынке. С переходом на TypeScript ничего не поменяется, ведь у нас по-прежнему остаётся большое количество кода, написанного на Dart, и разработчики рано или поздно должны будут столкнуться с ним. Так как мы ещё в самом начале пути, то наши инженеры будут сочетать написание кода на Dart и на TypeScript. 

Q: Почему вы не взяли фреймворк X или Y (Vue, Svelte etc)?

A: В свете микро-фронтендов выбор фреймворка не является чем-то, что высечено в камне. Однако, делая выбор, нам хотелось иметь максимально пологую кривую обучения. Большое количество наших инженеров уже знали и работали либо с React, либо с Angular, поэтому мы делали наш выбор в основном между этими двумя фреймворками. Но при микро-фронтендной архитектуре ничего не мешает нам в будущем рассмотреть и Vue, и Svelte, и остальные фреймворки.

Q: Будет ли Wrike в дальнейшем проводить конференцию DartUP?

A: Несмотря на то, что конференция основана благодаря инициативе и помощи Wrike, сегодня это событие вышло далеко за пределы компании. В России образовалось большое активное сообщество, а сам DartUP в 2020-ом году вышел на международную арену, организовав параллельный поток для англоязычных участников. Мы надеемся, что конференция продолжит жить и развиваться силами сообщества и компаний, не равнодушных к Dart и Flutter (в том числе и нашей), привлекая новых дартизан и флаттеристов со всего мира.

Заключение

Из-за причин, описанных выше, Wrike постепенно меняет свой стек для фронтенд разработки и движется в сторону TypeScript + React. Это не значит, что Dart плохой язык! Мы благодарны команде Dart и AngularDart за всё, что они делали и делают. Это было долгое путешествие, и Dart всё ещё остаётся важной частью нашего продукта. Мы желаем языку развиваться и достигать новых высот!

Подписывайтесь на наши социальные сети, если вам интересны более глубокие технические подробности и вы хотите следить за нашим путешествием в страну TypeScript.