⚛️ Как меня чуть не уволили за выбор React для корпоративного приложения
Грустная история о том, как неправильный выбор библиотеки привел к большим проблемам в разработке приложения.
Текст публикуется в переводе, автор статьи — Razvan Dragomir.
Летом 2018 года мы начали сотрудничество с одной крупной канадской компанией. У них было большое десктопное приложение на платформе WPF, и они хотели перенести его в облачный веб. Я должен был стать архитектором этого проекта.
Мы все обсудили, разобрали требования и выделили основные моменты для дальнейшей разработки:
- Приложение большое – больше 220 страниц. Большинство – это экраны технического обслуживания. Около 20 процентов страниц сильно кастомизированы.
- Необходимо выводить большие объемы данных (таблицы) и удобно с ними взаимодействовать: группировать, выделять и добавлять столбцы, расширять строки и т. д.
- Необходима модульная архитектура, чтобы сразу несколько команд могли работать над проектом.
- Планируется дальнейшее развитие приложения и добавление новых функций.
- Offline-функциональность не нужна.
- Важно, чтобы новые члены команды, особенно .NET-разработчики старого приложения, могли быстро адаптироваться к работе.
Я должен был разработать технический проект, включающий в себя общий подход, roadmap, гайдлайны и – самое главное! – стек технологий, который будет использоваться.
Основной выбор был между Angular и React. С обеими технологиями я уже работал и успешно реализовал несколько небольших и средних проектов на обеих. Заказчик не хотел Angular, так как не верил в него после устаревания AngularJS.
Для задачи я выбрал связку React + Redux… и очень пожалел об этом через 2 года.
Мы собрали команду из трех разработчиков, и сначала все шло просто замечательно. Пользовательский интерфейс был супер-отзывчивым, сборка – невероятно быстрой, а разработка – приятной. Все были счастливы.
Проблема #1: .NET-разработчики присоединяются к команде
После подтверждения технической концепции пришло время включиться в работу программистам из аутсорсинговой команды клиента. На первой же встрече они забросали меня вопросами и предложениями:
- «Где инъекция зависимостей? Что значит, в ней нет необходимости? Давайте возьмем InversifyJS!»
- «Нам не нравятся функциональные компоненты, давайте использовать нормальные классы».
- «Почему все эти функции просто висят вокруг и не инкапсулированы внутри сервисных классов как статические методы?»
- «Где политика повторных попыток для API? Давайте реализуем ее с помощью PollyJS».
- «Почему в именах файлов используется дефис, а имена классов в PascalCase? Имя файла должно соответствовать имени класса, поэтому мы теперь будем называть их SomePageComponent.tsx».
- И самое ужасное – «Как запустить проект в Visual Studio, а не в Visual Studio Code?»
Такое происходит очень часто. Разработчикам трудно адаптироваться к новым технологиям, поэтому они пытаются свои привычные .NET-шаблоны использовать в React. Но проблема в том, что для React эти паттерны необычны.
Мы долго обсуждали эти вопросы, и, к сожалению, мне пришлось пойти на компромисс и принять большинство их предложений.
В этот момент я пожалел, что не выбрал Angular. Он лучше бы подошел для Java или .NET-разработчиков.
Проблема #2: React никогда не ходит в одиночку
React – это библиотека, а не полноценный фреймворк. Он не предоставляет из коробки решения для многих сквозных проблем. Поэтому нужно выбрать массу сторонних инструментов, чтобы не изобретать собственные велосипеды.
Все это нужно было обсудить:
- Какой маршрутизатор использовать?
- Что использовать для асинхронных действий? Trunk? Saga?
- Axios или fetch browser API?
- Redux-Forms, Formiq или Final-Form?
- Styled-Components, makeStyle, SASS или простой CSS?
- Какую библиотеку использовать для интернационализации?
На принятие этих решений мы потратили три – ТРИ! – недели. Добро пожаловать в корпоративные проекты – каждое решение нужно обосновать и доказать его целесообразность. Это занимает безумно много времени.
Кроме того, каждый разработчик должен ознакомиться с этими сторонними библиотеками и разобраться в них.
Я никогда не видел двух проектов React с одинаковой структурой, зависимостями и гайд-лайнами. Это значит, что нет преемственности знаний между проектами.
Три недели в проекте не было никакого прогресса, и технический директор начинал беспокоиться.
Проблема #3: популярность React Hooks
За девять месяцев мы создали более 50 страниц.
Разработчики свыклись с функциональными компонентами и поняли, что они ничем не хуже классовых. Проект отступил от первоначальных гайдлайнов, теперь каждый выбирает то, что для него удобнее. И я считаю, что это нормально.
В это время в React появляются хуки. У команды по этому поводу смешанные чувства. Некоторые разработчики оскорблены идеей того, что классы путают и людей, и компьютеры. Другие с энтузиазмом относятся к новому шаблону кодирования.
Все сторонние библиотеки, которые мы используем на проекте, добавили поддержку хуков. Похоже, что весь мир React движется в этом направлении. И что нам делать? Нужно ли отклониться от первоначальных гайд-лайнов и добавить третий способ создания компонентов? Невозможно вернуться и переписать на хуки все существующие страницы и компоненты.
Команда выступила за использование хуков в Redux, так как это избавляет от необходимости использовать connect()
и отделять компоненты от контейнеров. В этом есть смысл, поэтому мы решили, что новые страницы и компонент будут использовать хуки, а старые останутся как есть.
В проекте больше нет последовательности – у нас целых три способа создания компонентов.
Еще хуже – некоторые разработчики начали продвигать идею больше не использовать Redux, заменить его на useState
. Это значит, что мы нарушим идею глобального состояния приложения.
React Suspense на тот момент все еще находился на стадии эксперимента, и я очень беспокоился о том, что произойдет, когда он официально выйдет.
Проблема #4: развитие замедляется
После настройки непрерывной интеграции сборка проекта занимала около трех минут, включая выполнение команды npm install
. Спустя год, она стала занимать около 15 минут.
Нам также пришлось настроить Node.js и расширить оперативную память до 4 Гб, потому что 2Гб уже не хватало.
Разработчики начали жаловаться на продолжительность сборки. Горячая перезагрузка в браузере начинала глючить после 45-60 минут разработки, а перезапуск проекта занимал более 5 минут, особенно на Windows. Иногда приходилось даже полностью удалять папку node_modules и устанавливать зависимости заново.
Все это неудивительно для проекта с более чем 1200 зависимостями общим размером 600Мб. Но корпоративное приложение крайне чувствительно к затратам. Если разработчик с почасовой ставкой $40 перезапускает проект 6 раз в день по 5 минут, в год для восьми разработчиков выходит $38 400. Это не очень большая, но весьма чувствительная сумма.
Проблема #5: Redux-saga медленно умирает
Хотя большинство разработчиков со мной не согласны, я считаю, что большая часть бизнес-логики находится внутри асинхронных действий Redux. Именно в них вы выполняете проверки, вызовы API, обработку ошибок, мутации и прочие действия. Если это не бизнес-логика в интерфейсном приложении, то что тогда?
Мы использовали Redux-saga, и это было плохим решением, так как добавляло ненужную сложность. Нам вполне хватило бы Thunk.
В корпоративных приложениях время от времени приходится обновлять и заново проверять зависимости. Это хорошая практика, так как позволяет проверить безопасность, улучшить производительность и провести небольшие инкрементные изменения API. И похоже, что Redux-saga перестала развиваться. Последнее обновление проекта было больше года назад, количество ишьюс на GitHub увеличивается, но никто их не исправляет.
Разработчики любят React по трем причинам: простота, гибкость и развитая экосистема. Команда React любит экспериментировать с новыми идеями, это прекрасно, но именно это и убивает экосистему. Сам React в основном обратно совместим со старыми версиями, но его экосистема – нет. Сторонние библиотеки всегда используют новейшие фичи и архитектурные шаблоны, а старые эксперименты медленно умирают. Для маленьких и средних приложений это обычно не составляет проблемы, так как они легко адаптируются. Но для больших многолетних проектов эти эксперименты могут стать критическим фактором.
В сентябре 2020 года я включил React-saga в результаты оценки рисков для руководящего технического комитета. Внутри саг находится 30% всей бизнес-логики, я считаю это высоким риском.
В этот момент технический директор вышел из себя и обвинил меня в принятии неверных решений в начала проекта. Эта искра разожгла и недовольство продакт-менеджера. Он засыпал меня сложными вопросами:
- «Почему мы должны тратить столько времени на обновление библиотек?»
- «Почему развитие проекта замедляется?»
- «Почему приложение стало глючным и нестабильным?»
Ситуация обострилась, и мне пришлось провести много времени в поисках доказательств того, что на момент начала проекта все решения были лучшим выбором.
К счастью, проект почти завершен и близок к переходу в режим технического обслуживания.
React – отличная библиотека. Я с удовольствием использую его для всех своих личных проектов и рекомендую для новых рабочих инициатив. Однако после этого неприятного опыта я не поощряю его использование для больших корпоративных приложений. И я в этом не одинок.