
Команда ЮMoney обслуживает около 410 вариантов писем: их собирает серверное приложение на NestJS, к которому обращается оркестратор, когда одному из примерно сотни backend-микросервисов нужно отправить пользователю email. Каждое письмо делалось руками, и добавление одной нотификации требовало правок примерно в 15 файлах — создать React-компонент, подключить его в модуле, описать параметры, добавить регистрацию и вспомнить ещё несколько мест. Фронтенд-разработчик Денис (на Хабре — Denisfg) описал, как команда заменила эту процедуру одной OpenAPI-схемой на письмо и генератором, который разворачивает из неё весь шаблонный код.
Результат измеряется во времени: сборка нового письма заняла минуты вместо часов. Сам выигрыш в скорости — следствие двух решений, которые полезно рассмотреть по отдельности: ограничение вариативности интерфейса четырьмя шаблонами и перенос описания параметров в формальный контракт.
Где терялось время
Пятнадцать файлов на письмо — это медленно и вдобавок хрупко. Однажды команда добавила несколько писем в одном pull request и забыла подключить их в модулях; дальше был долгий поиск причины, почему ничего не работает. Ручная регистрация в нескольких местах не проверяется компилятором, поэтому пропущенный шаг обнаруживался уже во время выполнения.
Вторая проблема — дрейф одинаковых по задумке блоков. Письма собираются из повторяющихся элементов: заголовок, описание, список, кнопки, ссылки, изображения. Дизайнер мог взять готовый компонент, а мог собрать похожий блок заново. У списка в одном письме появлялось нижнее подчёркивание, в другом его не было; изображение то лежало внутри контейнера, то выходило за его границы. По отдельности мелочи незаметны, вместе они увеличивали time-to-market и число согласований между разработчиками, дизайнерами и тестировщиками.
Четыре шаблона на 90% писем
Команда собрала существующие React-письма и вместе с дизайнерами выяснила: около 90% случаев закрываются четырьмя шаблонами. Каждый состоит из изображения, заголовка, описания, кнопок и ссылок, где необязательные элементы включаются и выключаются.
Шаблон здесь работает как ограничитель: внутри него зафиксированы размеры, отступы, типографика и список допустимых компонентов. Дизайнеру не приходится заново рисовать знакомые паттерны — он выбирает шаблон. Для оставшихся 10% писем возможность написать уникальный код сохранилась, и именно на этот порог команда советует ориентироваться: гнаться за стопроцентным покрытием смысла нет.
Один контракт вместо похода к соседней команде
Раньше, чтобы узнать, какие данные приходят в конкретное письмо, разработчик шёл к backend-разработчику, а если письмо принадлежало другой команде, путь удлинялся. Добавляла путаницы историческая деталь: на входе NestJS все параметры приводились к нижнему регистру. Приложению 11 лет, когда-то это было оправдано, а теперь удалить логику без последствий нельзя — разработчик ожидал walletNumber, а получал walletnumber.
Единой точкой правды сделали OpenAPI-спецификацию. Для каждого письма есть схема с бизнес-сценарием, letterId, параметрами, их типами и признаком обязательности, примерами и отдельными внутренними параметрами для обратной совместимости. Тестировщики и аналитики теперь смотрят параметры сами через Swagger. Для новых писем историческое приведение к нижнему регистру отключили: контракт уже задаёт имена.
Генерация из спецификации и проверка на сборке
CLI читает спецификации и разворачивает цепочку: OpenAPI → типизированная конфигурация → NestJS-сервис → React-компонент. Инструмент находит схемы, парсит YAML, приводит данные к типизированным объектам, создаёт на их основе сервисы и генерирует типы для React-пропсов.
Здесь появляется страховка, которой не было при ручной сборке. Если параметр изменился в OpenAPI, а компонент забыли обновить, типы перегенерируются во время билда и приложение падает на этапе сборки. Проверка согласованности контракта и потребителя переехала с ручного тестирования на компиляцию — ошибка ловится раньше и без участия человека.
Роль AI в этой схеме
Денис отдельно разбирает вопрос, почему письмо не пишет модель. Сгенерированный ею код может содержать не ту логику или пропущенную деталь, а ревью такого кода обходится дорого. AI применили на другом участке — он помог написать сам генератор. Дальше CLI работает одинаково для всех и выдаёт детерминированный результат: инструмент создавали с помощью модели один раз, повторяемость обеспечивает уже он.
Когда такой подход оправдан
Spec-driven генерация подходит не любой системе. Признаки, при которых её стоит рассматривать: добавление элемента сводится к копированию десятка похожих файлов; повторяющуюся часть можно отделить от уникальной бизнес-логики; параметры разбросаны по коду или держатся в памяти коллег и их реально вынести в формальную спецификацию; большинство экранов или сообщений собирается из общего набора блоков.
Ограничения команда называет прямо: нужно читать документацию, полностью описывать спецификацию и время от времени согласовывать с дизайнерами расширение шаблонов. Обмен состоит в том, что инженерное время тратится один раз на постройку системы, а экономится на каждой следующей нотификации. Запись доклада о кодогенерации на основе OpenAPI Денис выложил с митапа Frontend Mix.
Обсудить проект
Остались вопросы? Напишите нам
Оставьте имя и телефон — перезвоним в течение часа, разберём задачу и предложим решение.