Frontend: монолит, компоненты, фреймворк
Первая система — frontend tinkoff.ru: внешних пользователей у неё столько, сколько людей дошло до сайта, внутренних почти нет, зависимостей от чужих API много, а нагрузку Александр называет заметной, хотя хайлоадом не считает. Три года назад это было монолитное React-приложение: один репозиторий, около пятидесяти человек, писавших в нём код, и релиз, занявший при нём почти месяц вместо обещанных двух-трёх недель. Его собственная команда из трёх человек отвечала за формы продуктов и простые лендинги. Нулевой шаг он описывает как всегда одинаковый: познакомиться с командой, разобраться в процессах, показать тактический план. Первый шаг — выделить компоненты, вокруг которых пойдёт развитие: сам монолит, движок, собирающий страницы из блоков, новый движок форм взамен упершегося в ограничения и новую библиотеку трекинга, потому что бизнес-заказчики сравнивают варианты по конверсии и требуют событий.
Дальше монолит стали делить изнутри: код продуктовых команд разносили по отдельным каталогам, а общие компоненты вытаскивали в зависимости — этим активно занималась общая архитектурная команда. Одновременно новые компоненты возвращались в монолит как зависимости: результат видно быстро, и бизнесу не приходится согласовывать дорогой переезд с непредсказуемым результатом. Александр сравнивает это с каркасом Запорожца, у которого внутри уже совсем другое. Последний шаг — собственный фреймворк, куда сложили и свои наработки, и вытащенное из монолита, чтобы каждая продуктовая команда получила отдельное приложение и репозиторий поверх общих зависимостей. Проверяли на важной команде, где ценность для бизнеса видна быстрее всего: копию приложения на новом подходе выкатили в тест, трафик поделили примерно поровну — и событий зарегистрировалось больше, потому что новое приложение работало быстрее. Вывод: не застывать в полупозиции, когда новые компоненты живут в старом монолите.
Контентный backend: от AngularJS к Kubernetes
Вторая система готовит контент для фронта и мобильных приложений, а внутренние пользователи правят его через админки; к концу истории в команде было около пятнадцати человек. Три года назад это было монолитное AngularJS-приложение: его начинали как API для себя, на том стеке, который знали, кодовая база выросла небольшой, а команда была из трёх человек. За год она сделала два релиза, без обратной совместимости, версионирования, логов и мониторинга — ничего из того, что стоит применять в бэкенде. Главным внутренним заказчиком были веб-аналитики, которым нужно было менять контент продуктов без релизов фронта, а те шли неделями. Первый шаг — стабилизация: систему классифицировали как legacy, зависимые команды страдали, а из-за сильной связанности правка ради мелкого заказчика прилетала крупному. Дальше распил: контентный backend на Node.js, потому что рядом много React-разработчиков, и PostgreSQL вместо унаследованной базы.
Под сложные решения выделили архитектурную команду — и она, по словам Александра, разгулялась: спроектировала большой дом, а продуктовые команды раздали по сервису на разработчика, и каждый вышел уникальным произведением искусства. Выкатывать десятки таких сервисов оказалось некуда, жили на docker-compose; про эти проблемы он, по его словам, рассказывал в отдельном докладе примерно за полтора месяца до ArchDays. Архитектурную команду в прежнем виде расформировали, сервисы привели к двенадцати факторам и Kubernetes, а инженеры предложили типовой backend, который конфигурируется YAML вместо программирования, и простой BFF: снаружи сервисы стали почти одинаковыми. В этом году часть из них уехала с собственных машин в Kubernetes и в облако Google — это можно, потому что там лежит контент страниц, а не персональные данные. Монолит всё ещё жив: последний кусок функционала должна доделать и выкатить другая команда, и только после этого систему можно погасить — Александр мечтает распечатать её исходники и пожарить на них шашлыки.
Персонализация и вопросы из зала
Третья система маленькая: она обогащает запрос гео и данными об устройстве, поднимает известное о пользователе — клиент ли он, есть ли продукты и брошенные заявки — и решает, что показать. Внешний потребитель у неё один, контентный backend, зато запросов много, а команда — человек пять. Начиналось с коробочного продукта, названия которого Александр не вспомнил, и собственного движка на C++, написанного одним разработчиком; своя система была нестабильной и не повторяла функциональность коробки. Команду расширили, MVP добили, лицензию продлевать не стали — но, по его словам, хотели машинку, а сделали пока велосипед: прототип не был рассчитан на развитие, а интерфейс делали отдельно, и он не вставал в сценарий, где пользователь создаёт страницу и тут же настраивает персонализацию. Движок переписали с нуля конвейером шагов в духе UNIX, интерфейс отрефакторили и с трудом вписали в контур управления контентом.
Отсюда вывод: MVP пишите на чём хотите, но планируйте выбросить, а заодно помните про закон Конвея — систему растили в отдельной команде с отдельным менеджером, и потом она интегрировалась как шов после операции. В зале спросили, почему сработала схема с backend: бизнес не согласился бы ждать год ради микросервисов, поэтому старую систему чинили и катали в неё горящие фичи, а рядом растили новую и закрыли её фасадом со старым API. Про массовое обновление зависимостей: на фронте блоки страницы меняются конфигурацией на лету, а на backend вместо эпика с ворохом задач хотят фитнес-функции — правило, предупреждение в пайплайне пару недель, потом запрет релиза. Сроки Александр оценивать отказался: планировать, не зная глубины кроличьей норы, нельзя. Гибкий DSL для связей полей в первой версии движка форм почти не использовали и выпилили как оверинжиниринг. Релизы теперь считают по всем приложениям сразу: на десяток фронтовых команд выходит один-два релиза в день, хотя одной команде на набор фич обычно нужна неделя. Распределённых транзакций у них нет, и это, по его словам, хорошо.
Что стоит унести с собой
- 01Размер системы диктует стратегию: маленький сервис персонализации переписали с нуля, средний backend догоняли параллельной разработкой с фасадом над старым API, большой frontend меняли по частям, вставляя новые компоненты в монолит.
- 02Инжект новых компонентов в старый монолит даёт быструю обратную связь, но это промежуточная станция: цель — отдельные приложения и репозитории продуктовых команд поверх общего фреймворка.
- 03Микросервисы стоит начинать с правил и плана эксплуатации: без договорённостей каждый сервис выходит уникальным, а деление по сущностям вместо границ контекста даёт неконсистентные данные и просьбу слить всё обратно.
- 04MVP делается на чём угодно, но с расчётом на выброс: прототип, который решили развивать как продукт, обходится дороже, чем движок, переписанный начисто.
Источники
- Слайды материала
- Запись выступления