Спустя полгода адаптации к OpenSource выпускаю еще две библиотеки из моего рабочего контура в одном npm-пакете - ARFA Generator и api-contract-check. В этой статье расскажу про первый инструмент, а второй презентую в рамках отдельной статьи.
Когда-то давно я уже писал статью про удобство использования файловых генераторов, интегрированных в долгоживущий процесс dev-сборки, однако за несколько лет развития и обкатки в реальных проектах концепт существенно расширился. Удобнее всего оказалось сместить бандлер с центрального места в один из сайд-эффектов после стабилизации кодовой базы, а разнородные инструменты проверки качества объединить в единый пайплайн с инкрементальным watch-режимом.
В статье не буду пересказывать документацию на русском и английском, а расскажу, как я пришел к такой схеме, насколько она улучшает DX, помогает людям быть продуктивнее, LLM-агентам сжигать меньше токенов, а кодовой базе не расползаться со временем и проще мигрировать между архитектурами.
1. Бандлеры захватывают цикл разработки
Во времена Gulp процесс бандлинга воспринимался как последовательная серия трансформаций исходного кода, результат которой - подготовленные для браузера файлы:
browserify('./index.js').bundle() .pipe(source('bundle.min.js')) .pipe(buffer()) .pipe(terser()) .pipe(gulp.dest('dist'))
Сервер для разработки и перезагрузка браузера настраивались отдельными программами. Впоследствии Webpack / Vite решили скрыть процедурный пайплайн за декларативной конфигурацией и внедрили сервер + HMR, получив название "бандлеры". Со временем именно они стали дефолтной точкой входа в процесс разработки, однако роль инструмента не изменилась - это "часть пайплайна, которая упаковывает артефакты для браузера", то есть просто операции над стабилизированным исходным кодом.
Мысль: у библиотек для сборки есть давно определенная роль, и их вполне можно встраивать в более широкое ядро разработки, учитывая, что сейчас и дев-сервер, и перезагрузка браузера по SSE пишутся в несколько строк и не являются ограничением.
2. Исходный код - тоже артефакт
Иногда создается ложное ощущение, что наш код - истина в конечной инстанции. Да, конечно он трансформируется транспилятором, фреймворками (особенно это видно по шаблонам jsx или vue), переименовывается минификатором, но у программиста остается полный контроль и единоличная ответственность.
Однако то, что мы называем "тулингом" давно и прочно вошло в разработку. Eslint, Prettier, Oxc, Biome получили возможность не только давать рекомендации по исправлению кода, но и напрямую его редактировать. TSC, bun check, tsrs - контролируют корректность типизации и заставляют переписать код. Есть и много других детерминированных анализаторов и инструментов, которые напрямую влияют на вид и содержание кода, включая codegen-утилиты.
Это хороший тренд - ответственность теперь не только на одном разработчике, а "коллективная" - насколько качественно настроишь инструменты, настолько это снизит пространство ошибок в программе и улучшит условно-измеримые характеристики исходного кода вроде "согласованности между всеми разработчиками в команде" и "понятности для коллег". Но, к сожалению, все эти библиотеки - отдельные cli инструменты, и не имеют общего канала предоставления отчетов.
С приходом LLM у исходного кода появился еще один редактор с непредсказуемыми изменениями и собственными ограничениями, часть которых компенсируется именно детерминированными инструментами.
Мысль: на исходный код влияет множество участников с разной степенью контроля и предсказуемости, но они не объединены в единый пайплайн и не имеют общей обратной связи или истории внесенных изменений.
3. Бойлерплейт и пустая трата времени
Возможности детерминированного контроля исходного кода в настоящее время сильно недооценены. В процессе разработки любого проекта возникают рутинные задачи:
при написании нового редюсера нужно его добавить в общий файл и написать экшен
если в проекте договорились импортировать утилиты из одной точки, добавленный getTime.ts нужно не забыть добавить в реэкспорты
новые тексты в интерфейсе нужно синхронизировать с облачной системой переводов, не забывая удалять неиспользуемые ключи
присланные бэкендом данные необходимо валидировать и очищать от лишнего, чтобы не сломался Object.keys в легаси-коде
при изменениях нужно поддерживать актуальность документации и т.п.
Я привел лишь небольшое количество сценариев - в документации ARFA Generator приведены решения для десятков типовых задач: из структуры файлов, их наименования и контента можно вывести большое количество потенциально автоматизируемых операций. Сегодня разработчики вынуждены выполнять их вручную из-за отсутствия подходящих инструментов, времени для их создания, или потому что "здесь всегда так делали".
Мысль: можно значительно сократить ручную работу и уменьшить когнитивную нагрузку и без использования LLM-агентов.
4. Архитектура - тоже артефакт
Существует множество определений "архитектуры", но применительно к фронтенду обычно подразумевают "структуру файлов и модулей, включая именование", "границы ответственности" и "организацию потока данных". Часто это содержится только в устных договоренностях или документации, но может закрепляться в статическом анализе вроде eslint boundaries или даже выбрасывать исключение в рантайме (что часто делают в React при попытке подключиться к недоступному контексту).
Однако эти механизмы не дают целостного представления, и с ростом кодовой базы становится все сложнее отслеживать расхождения и дублирование. Для меня же основные критерии качества архитектуры - то, что ее можно сериализовать из кода, восстановив большую часть исходных правил; при необходимости без особых сложностей заменить на другую; детерминированно проверять соответствие принципам в едином пайплайне:
Первый инструмент проверяет, что экспортируемая функция / константа / стор именованы согласованно с названием файла
Второй строит сводную карту слоев (например, всех сторов, экшенов, роутов) или модулей / фич, по которой легко проверить дубляжи, несогласованный нейминг или случайно появившиеся лишние сущности
Третий проверяет соблюдение границ импортов
Четвертый по шаблону создает бойлерплейт или каркас новых модулей в соответствии с выбранным подходом
Пятый строит карты рантайма, показывающие, например, какие сущности доступны на конкретной странице. Это упрощает ревью и в ряде случаев позволяет быстрее найти проблемы
Шестой автоматически актуализирует документацию
При этом пайплайн должен уметь показывать и при необходимости удалять автоматически контролируемые файлы, позволяя изменить правила и легче мигрировать на другую архитектуру.
Мысль: фиксировать соглашения удобнее не в документах, а в детерминированных правилах с общей историей нарушений и списком контролируемых файлов.
5. Единый пайплайн
Итак, все предыдущие мысли наводят к закономерному выводу: ядром процесса должен быть не бандлер, который просто делает одну из операций, а реактивный инструмент, объединяющий разрозненный тулинг в единый процесс. Тут и выходит на сцену ARFA Generator, которым я пользуюсь в крупных проектах уже много лет, и который заиграл новыми красками после распространения ИИ. Он работает со следующими слоями:
Плагины - это мутирующие исходный код инструменты. Они запускаются итеративно - в цикле каждый из плагинов получает данные о модификациях, проведенных другими плагинами, пока кодовая база не стабилизируется. Могут писать, модифицировать и удалять файлы
Чекеры - это немутирующие исходный код инструменты, например tsc / eslint / test-runner / AST rules enforcer. Выполняются после плагинов одним проходом - здесь нет необходимости в каскадных итерациях
afterCheckers - немутирующий исходный код колбэк, подходит для запуска бандлера в едином пайплайне
vite.build / esbuild.build / bun.build, то есть для превращения стабилизированного и проверенного кода в другую форму - для браузера, документации или внешнего анализаafterFinish - потенциально мутирующий код колбэк. Получает логи всех предыдущих этапов для вывода в консоль, а также здесь оптимально вызывать перезагрузку браузера или сигнализировать другим системам, что процесс полностью прошел. Можно вызвать и LLM-агента, который изменит файлы и дальше запустит новый полный цикл
Этот пайплайн можно вызывать единожды (для CI) или в watch-режиме для локальной разработки. Для LLM-агентов также есть отдельная точка входа в виде команды cli, через которую можно получить информацию о последнем цикле в markdown-формате с дефолтными инструкциями "исправлять первую значимую ошибку". При этом в самом пайплайне уже зафиксирован вызов определенных инструментов для каждого файла - ИИ не нужно это предсказывать, а человеку - задумываться при разработке.
Также слежение за всем исходным кодом, включая добавление новых файлов, позволяет сразу их включать в архитектуру, в отличие от watch-режима бандлера, который не следит за файлами, отсутствующими в графе импортов (или следит, но никак не учитывает и не встраивает в известный граф).
6. Почему единый пайплайн стал возможным
За недолгое время существования веб-разработки тулинг значительно развился. Раньше мысль о подобном пайплайне скорее пугала - ведь внедрение всех этих операций в цикл разработки означал бы не только 40 условных секунд пересборки на Webpack, а плюс столько же времени работы каждого из инструментов. Поэтому большинство разработчиков использовало схему дев-разработки через watch режим бандлера - это давало хоть немного приемлемую производительность, а остальные инструменты запускались на pre-commit или в CI.
Однако сейчас инструменты становятся настолько быстрыми, что эта схема является устаревшей. Сейчас пайплайн из плагинов + bun check / tsgo + Biome + bun test + esbuild для проектов размером около 100 страниц на Реакте занимает меньше 2 секунд (по замеру на ряде проектов, которые делал), а кроме того ARFA Generator дает механизмы их запуска только с изменившимися файлами, что еще больше ускоряет операции. Таким образом, и человек, и LLM-агент очень быстро получают фидбек ото всех инструментов без дополнительной когнитивной нагрузки / сжигания токенов на то, какой инструмент сейчас стоило бы вызвать.
7. Пропущенный этап развития тулинга
Верхнеуровнево для меня процесс обучения веб-разработке и в целом глобальное развитие подходов в разработке выглядит так:
Обучение с нуля, все пишется руками. В мире это означало отсутствие готового тулинга и были только базовые примитивы в виде языков программирования и browser API
Изучение готовых фреймворков и библиотек. Это время становления экосистемы и разделения мира фронтенд-разработчиков по фреймворкам, развитие tsc / eslint и другого тулинга
Понимание ограничений и написание своих библиотек, решающих ряд необходимых задач эффективнее. В мире увеличивается количество разработчиков, инструментов, создается много компактных альтернатив раздутым "традиционным" решениям
Детерминированная автоматизация вроде ARFA Generator для гибкого объединения инструментов и избавления от надоевшего ручного бойлерплейта
Эпоха недетерминированной генерации на основе уже достаточно проработанных и стабильных исполняемых правил в проекте
И вот четвертый этап во фронтенде почему-то недопрошли - его мало кто применяет в реальности и еще меньше тех, кто о нем пишет или публикует библиотеки. При этом проблемы, перечисленные в этой статье никуда не делись, а ИИ через фразу указывает на отсутствие формализованных правил и механизма их передачи. Уже приевшийся термин harness пытается это решить "текстовыми описаниями архитектуры в md файлах", "правилами, какой инструмент вызывать при изменениях", "описанием loop до исчезновения всех ошибок" и т.п. Но все понимают, что это мягко говоря неэффективно при работе с недетерминированными и часто меняющимися LLM.
Когда я столкнулся с LLM, долго не понимал куда его применить, учитывая что тогда ее возможности были ограниченны:
"Избавить от ручного бойлерплейта" - я и так пишу только значимую бизнес-логику, остальное детерминированно, бесплатно и быстро генерируется
"Набросать черновики" - очень специфичная задача, как правило после груминга в голове уже выстраивается карта необходимого решения с учетом специфики проекта и мнений коллег, а в компаниях производящих "экспериментальные микро-mvp для быстрой проверки гипотез" я не работал
"Написать бизнес-логику" - задачи часто приходят с недостаточным описанием, и куда быстрее написать код самому, чем продумать и создать полноценное "описание кода на естественном языке"
Сейчас возможности моделей и агентов сильно улучшились, поэтому добавил в ARFA Generator cli-режим фидбека, хотя в продакшене все еще очень мало им пользовался - все еще быстрее написать руками, чем читать много чужого текста и кода, но возможно кому-то будет полезно.
Для быстрой проверки, чем эта библиотека может быть полезна в вашем проекте - передайте ИИ llms.md, это единый файл с документацией и примерами, чтобы не тратить токены на Vitepress html разметку. Вот ссылка на репозиторий, а я пойду неспешно писать статью про следующий выложенный инструмент - он тоже довольно интересный и концептуальный.
