Это продолжение первой статьи про взрослый вайб-код для тех разработчиков, которые уже перешли на тёмную сторону. Ради неё, на самом деле, писалась первая часть: меня очень попросили опубликовать свой ИИ-фреймворк, в котором сейчас производится 99% моего кода, поэтому делюсь тут.
Меня зовут Саша Раковский. Работаю техлидом в расчётном центре одного из крупнейших банков РФ, где ежедневно проводятся миллионы платежей, а ошибка может стоить банку сотни миллионов рублей. Законченный фанат экстремального программирования, а значит и DDD, TDD, и вот этого всего. Штуки редкие, крутые, так мало кто умеет — для этого я здесь, делюсь опытом. Если стало интересно, добро пожаловать в мой блог.
В прошлой части я разобрал базу по ИИ-разработке для начинающих и накинул несколько тезисов от себя:
-
На голых промптах далеко не уедешь. В ИИ-разработке необходим системный подход к решению повторяющихся задач, исправлении повторяющихся ошибок, в управлении контекстом. Без ИИ-фреймворка — никак.
-
Без автотестов хоть сколько-то адекватная ИИ-разработка невозможна. Именно автотесты должны быть центральным элементом ИИ-разработки. Если софт — это совокупность поведения системы, то зелёный автотест — это единица поведения, фундаментальная частица, из которой построен этот софт.
-
Автономная ИИ-разработка — это пока фантастика. Даже самые лучшие модели чудят так, что никакими тестами не предугадаешь все их возможные косяки. Человеческое ревью необходимо обязательно.
В общем, нужен фреймворк, построенный вокруг автотестов и частого ревью. Чем больше кода необходимо отсмотреть, тем выше вероятность пропустить какой-то косяк. Поэтому чем чаще ревью, чем меньше смотреть за раз, тем лучше.
Из опенсорсных TDD-фреймворков мне известен только один — Superpowers. И это вполне годная штука, если вы предпочитаете легковесные подходы в дизайне и автотестах, но при этом хотите поддерживать высокую дисциплину разработки.
Однако, именно поэтому мне, например, superpowers не очень подходит — слишком уж легковесный. Он практически противоречит ATDD и плохо встраивается в более структурированные процессы разработки. В то же время, в процессе разработки у меня как-то само по себе родилось своё решение, заточенное под принятый в нашей команде «книжный подход».
И раз получилось неплохо, то почему бы, по просьбам подписчиков своего канала, и не поделиться прогрессом на текущий момент. Фреймворк проверен на 3,5 месяцах работы, 4к коммитов, 1500 тестов, ~350 — e2e, 25к строк продакшн-кода (12k java back, 12k ts/tsx front). И столько же тест-кода.
Описание
Начнем с концепции. Любой софт — это совокупность поведения системы. Каждая единица поведения должна иметь свой тест-кейс. Зелёный, то есть подтвержденный. Поэтому хороший софт — это совокупность зелёных тест-кейсов. Именно вокруг этой идеи построены процессы разработки в нашей команде. И именно она легла в основу самого фреймворка.
Верхнеуровнево фреймворк проходит следующую последовательность шагов:
-
краткое описание продукта + перечисление историй
-
история 1
-
этап спецификации
-
интервью
-
… (проектирование требований на основе интервью и описания продукта)
-
генерация кейсов на основе требований
-
-
этап реализации сгенерированных кейсов:
-
кейс 1
-
красный тест
-
… (реализация)
-
зелёный тест
-
-
кейс 2
-
… (остальные кейсы)
-
-
-
история 2
-
этап спецификации
-
…
-
-
этап реализации
-
Вся работа разбита на истории — минимально зависимые друг от друга фичи, каждая из которых поставляет свою рбособленную ценность. Как говорил Кент Бек пользакам: «Tell me your story». История должна покрывать какой-то один аспект пользовательского опыта. Например, «однажды я забыл пароль» — вполне себе подобная история, которая превращается в фичу «Сброс пароля».
Разработка истории начинается с постановки требований: описания истории, критериев приёмки, проектирования апи, макетов. Эти требования необходимы для составления списка тест-кейсов — главного артефакта для каждой истории.
После того, как тест-план готов, фреймворк проходит по списку кейсов в TDD-стиле, реализуя тесты один за другим, прерываясь лишь на ревью. После того, как все тест-кейсы позеленены, история считается реализованной.
/continue
Центральным элементом фреймворка является скилл /continue, который, опираясь на текущее состояние проекта — список историй и их прогресс-файлы, определяет, какой дальнейший шаг необходимо сделать.
По ссылке, я специально сделал демо-пример классического бэк+фронт приложения — Kanban-доски с 3 запланированными к разработке историями в stories.md:
-
Создать таску.
-
Передвинуть таску в другую колонку.
-
Изменить/удалить таску.
Пройдёмся по ним в обратном порядке:
-
К истории #3 я намеренно не приступал, поэтому
/continue 3начнет проектирование с самого первого шага — написания спецификации:-
проведёт и запротоколирует интервью;
-
составит описание истории;
-
построит макеты интерфейса;
-
определит используемое апи и спроектирует новое, если надо;
-
сгенерирует тест-кейсы, необходимые для реализации.
-
-
В истории #2 спека уже есть, но разработка еще не начата, поэтому «/continue 2» начнет уже с разработки бэка: найдет первый кейс, прочитает спеку, напишет красный приёмочный тест и побежит шаг за шагом его зеленить в коротких red-green-refactor TDD-циклах, прерываясь на ревью после каждого шага.
-
В истории #1 уже сделаны все кейсы на бэке и даже начат фронт, поэтому вызов скилла «/continue 1» приведёт к тому, что разработка продолжится с фронтового кейса — с последнего шага, который отмечен в progress.md истории, как выполненный.
Но, как видите, тут мы встречаем первое ограничение: фреймворк был разработан мной для своих задач — разработки серверного приложения. Чтобы использовать фреймворк для мобильного, десктопного приложения, embedded, игр или для еще какого-то софта, может понадобиться доработка фреймворка через специально придуманный для этого скилл /prompt-update.
progress.md
В каждой истории есть свой progress.md, который фиксирует, на чем остановилась работа над историей в прошлый раз. Это длинный чек-лист, разбитый на несколько фаз:
-
спецификация
-
реализация:
-
бэкенд
-
внешние интеграции
-
фронтенд
-
безопасность
-
нагрузка
-
инфраструктура.
-
Спецификация состоит, как я уже говорил, из нескольких шагов: интервью, описания, макетов, апи и тест-плана.
После каждого шага /continue, отмечает шаг сделанным в чеклисте, делает коммит и встает на паузу, ожидая ревью от человека. После запуска /continue переходит к следующему шагу.
Все остальные фазы состоят из кейсов, сгенерированных, в первой фазе.
TDD-ядро
В свою очередь, каждый кейс разбивается на отдельные TDD-шаги. Тут уже фреймворк очень сильно начинает навязывать свой подход к разработке — а именно ATDD, процесс в котором большой TDD-цикл оборачивает внутри несколько маленьких.
Например, для бэка это выглядит так:
-
красный приёмочный (e2e, acceptance) тест;
-
красный usecase-тест;
-
зелёный usecase-тест;
-
красный адаптер-тест (например, контроллер);
-
зелёный адаптер-тест;
-
… такие же мини-циклы для других адаптеров
-
-
зелёный приёмочный тест;
После каждого шага следует коммит, чтобы можно было посмотреть изолированные изменения, сделанные в рамках отдельного шага.
Архитектурные правила
Как видим, тут навязана Clean Architecture с общепринятым в гексагональной архитектуре ATDD. Этот подход к тестированию описан, например, в «Get Your Hands Dirty on Clean Architecture», Tom Hombergs и «Hexagonal Architecture Explained» от самого автора гексагоналки, Alistair Cockburn. Если вы предпочитаете иную архитектуру или иной тестовый подход, то тут потребуется доработать фреймворк под себя с помощью /prompt-update, о котором я уже упоминал, но подробнее расскажу чуть позже.
Quality gates
Одно из больных мест любой LLM — это качество кода, на котором она обучается. Поэтому для исправления типовых косяков LLM, мне пришлось добавить еще несколько гейтов, чтобы исправлять типовые ошибки:
-
на красном шаге это ревью строгости тестов и соблюдения тестовой архитектуры + рефакторинг;
-
на зелёном шаге это рефакторинг + проверка покрытия тестами.
Каждый гейт содержит обязательный чеклист, по каждому пункту которого LLM должна отписаться, есть ли нарушение или нет.
После всех гейтов следует коммит, и в дело вступаем мы, отсматривая diff коммита и высказывая замечания. Если замечаний нет или они устранены, мы сбрасываем контекст и делаем /continue. Фреймворк молотит дальше, а мы идем в другое окно IDE разруливать другой добежавший до конца /continue.
Контекст-менеджмент
Каждый шаг реализации разбит на 2 этапа: написание теста/кода и последующие гейты (ревью, рефакторинг, coverage). Чтобы эти активности друг на друга не влияли, все подшаги запускаются в отдельных агентах.
Другой приём управления контекстом — этот тот самый progress.md, позволяющий сбрасывать контекст после каждого шага, и начинать с пустого контекста, загружая туда только самое необходимое, повышая надежность вывода.
Самоулучшение
Если я сталкиваюсь с каким-то косяком не в первый раз, я вызываю /prompt-update с описанием проблемы. После исправления фреймворка, я отсматриваю изменения, откатываю последний коммит, сбрасываю контекст, и проверяю, что в этот раз проблема решена.
В итоге, день за днём фреймворк обрастает всё новыми и новыми правилами, становясь всё более эффективным… и, порой, все менее послушным. Потому что чем больше правил, тем больше шансов, что какое-то из них нейросеть простодушно забудет.
Как попробовать
Сейчас я пишу код только с этим фреймворком и без него уже вряд ли захочу. Если хотите прикоснуться ко всем тем книжным процессам, которые работают у нас в команде, и повертеть в руках не хаотичный вайб-кодинг, а полноценный взрослый продакшн процесс ИИ-разработки, то я специально приготовил 2 репозитория:
Независимо от родного языка и типа проектируемых систем я бы рекомендовал сначала всё же скачать первую репу и поиграться с ней.
Рекомендуемый сценарий:
-
Я предполагаю, что у вас есть подписка на клод или на клод-совместимую LLM (например, GLM5 от Z.ai или вы умеете запускать Claude-конфигурацию в Codex).
-
Склонируйте репу с Канбан доской. Рекомендую склонировать 3 раза и открыть каждую репу в отдельной IDE. Ну или используйте worktrees, если так привычнее.
-
В каждой из IDE откройте терминал, зайдите в клод, и введите
/continue 1,/continue 2,/continue 3соответственно. -
В первой ide клод приступит к написанию selenium-теста для фронтового кейса, на котором остановилась разработка.
-
Во второй ide начнёт разработку с того места, где остановилась вторая история — с acceptance-теста для первого бэкенд-кейса.
-
В третьей ide клод увидит, что третья история ещё не начата и приступит к разработке с самого первого шага — интервью.
Про параллелизацию
Почему 3 ide?
Пока /continue делает все свои необходимые шаги, легко может пройти от 5 до 20 минут. Иногда, если клод столкнулся с какой-то непростой для него проблемой и по много раз перезапускает долго исполняющиеся тесты, чтобы проверить ту или иную гипотезу, время одного запуска может затянуться на сорок минут и даже час.
Это один из недостатков получившегося фреймворка — цикл, на который Claude уходит в работу, очень долгий. Впрочем, если дефицита задач нет, то параллелизация работы позволяет все равно двигаться именно с той скоростью, с которой вы способны ревьюить изменения. Каждая фича будет ехать примерно, как если бы она была написана руками, но одновременно с ней будут ехать еще 3-4 минимум.
Впрочем, уже сейчас многие модели переходят на заметно более высокие скорости ответа. Так что в ближайшие полгода-год, вероятно, этот недостаток уйдет в прошлое.
Почему не worktree?
Можно и worktree, конечно. Но я использую отдельные репозитории, потому они чуточку удобнее и позволяют чуточку больше.
Как у меня
Мой типовой поток состоит из 6 параллельно запущенных IDE, в которых 4-5 молотят /continue или обрабатывают замечания по результатам работы этого скилла, а в оставшихся я занимаюсь какими-то техдолгами: планирую рефакторинги, ищу проблемы в коде, оптимизирую пайп.
Про порты
Поскольку параллельно запущенные агенты начинают одновременно запускать приложение, чтобы погонять тесты, то тут происходит конфликт портов и общей инфраструктуры. Чтобы этого не происходило, предусмотрены скрипты, определяющие порты на основе имени директории репозитория.
Если папка будет иметь на конце индекс: «continue-example1», «continue-example2», «continue-example3», то это позволит фреймворку забить для каждого репозитория порты так, чтобы при запуске приложений и инфраструктуры разные потоки друг другу не мешали.
Как стартануть с нуля
Альтернативный вариант — пустая репа с голым фреймворком. Я запарился и потратил кучу времени на отладку и тестирование фреймворка, чтобы он, независимо от выбранных технологий, мог доехать от пустой репы, до простенького mvp на одних лишь только вызовах /continue и небольших корректирующих усилиях по причесыванию каких-то заскоков.
Что делать?
-
Клонируете пустой фреймворк.
-
Прописываете:
-
описание продукта в BriefProductDescription.md
-
ожидаемую нагрузку/производительность в ExpectedLoad.md
-
технологии в technology.md
-
генерите вместе с клодом список историй и просите его записать их в stories.md
-
-
И дальше /continue без остановки.
У меня отняло несколько вечеров, чтобы отвязать фреймворк от этой своей джавы и сделать его более-менее рабочим на других языках. Я сделал несколько шаблонов под 90% основных технологий и языков. И оно даже работает на всех из них плюс-минус так, как я ожидаю. Но если вы с другого языка, то вы, вероятно, можете испытать некую форму культурного шока от того, как результат не похож на то, что принято в вашем языке. Так что /prompt-update вам в помощь.
Полезные фишки
Помимо /continue и саб-скиллов, которые он оркестрирует, есть еще парочка полезных скиллов, которые вызываются вне основного потока /continue.
/prompt-update
Этот скилл используется для любого исправления поведения фреймворка. В том числе, для самых серьезных переделок фреймворка. Этот скилл один из самых вызываемых — я его использую по несколько раз в день.
Рядом с ним еще живёт /prompt-refactor, который предназначен для поиска проблемных мест во фреймворке. Но этот скилл я вызываю нечасто: порой он предлагает ерунду всякую, поэтому может использоваться только в условиях глубокого понимания работы фреймворка и контекста. Например, чтобы оценить последние изменения: /prompt-refactor last commit.
/task
Не всё то история, что надо кодить в tdd-стиле, итерируясь по прогресс-файлу. А потому довольно скоро у меня появился скилл, предназначенный для багфиксов, рефакторинга, работы над инфраструктурой, промптами и иных задач, требующих продолжительной работы. Концептуально, таска близка к истории, но содержит гораздо меньше церемонии, подразумевая только описание задачи и прогресс-файл, содержащий исключительно необходимые для реализации задачи шаги.
Скилл /continue умеет итерироваться по задачам так же, как и по историям. Таски хранятся в своей папочке и после реализации мигрируют в папочку /done. Я в своей разработке использую его не реже раза в день.
/doc
Одно из не самых очевидных применений агентских систем — аналитика. Клод замечательно справляется с гуглежом документации, исследованием апишек, их проверкой с помощью курла. Клод может может сделать полную локальную копию больших объемов текстовых данных. И даже может скрейпить сайты, хитро обходя всякие антибот системы сайтов, чтобы собирать оттуда необходимую информацию.
Также Клод неплохо справляется с аналитическими задачами на больших данных — делегируя вычисления в код, оставляя за собой только формулировку правил, верхнеуровневый поиск каких-то закономерностей и формат отображения.
Всё это очень сильно упрощает и ускоряет аналитику. Но вот проблема: результаты этой аналитики потеряются вместе с сессией, а контекст сессии с каждым запросом к ней будет засоряться. Поэтому для фиксации всех своих находок может быть полезен скилл /doc.
Портирование и ограничения
У фреймворка есть много ограничений:
-
он был разработан под конкретный технологический стек;
-
под определенные подходы к разработке — Clean Architecture + DDD + OOP + Martin Fowler’s Refactoring Book;
-
под определенный подход к тестированию, ATDD — Acceptance Tests (Continuous Delivery book) + hexagonal architecture testing (adapter tests + application tests);
-
под бэк + фронт веб-приложение;
-
для экономии токенов используется английский язык.
В целом, если от технологии я его как-то отвязать смог, то ни от Clean Architecture, ни от ATDD даже пока не планировал. Поэтому, если Clean Architecture и ATDD для вас чужды, то, возможно, проще будет попытаться под себя адаптировать SuperPowers.
Но, если уж вам понравилось решение, то для редактирования фреймворка под свои задачи, вам потребуется часик-другой доработать его напильником скиллом /prompt-update.
Техника безопасности
Ну и, как обычно, в завершении техника безопасности:
-
ни ИИ, ни фреймворк не заменит разработчика: если на ревью не будет замечен баг, то он уедет в прод и все сломает;
-
фреймворк работает по принципу «железо дешевле людей» и предпочитает сэкономить человеческое время, потратив токены, поэтому мне не всегда даже $200 подписки Claude хватает для 5 часового окна при работе в несколько потоков;
-
фреймворк исполняется ллмкой, ллмки нарушают правила, поэтому иногда фреймворк будет сходить с рельс, и надо его подправлять (самое частое — иногда требуется повторно дёрнуть скилл /refactor или /test-review на чистом контексте);
-
фреймворк был разработан под один конкретный проект; он может показать себя заметно хуже на другом языке, на другой архитектуре, для других задач и в другом подходе к тестированию;
-
фреймворк разработан под вкусовые предпочтения автора, если у вас другие — /prompt-update или superpowers;
-
фреймворк молодой, не учитывает и не может учитывать всего, поэтому он нуждается в постоянной доработке с помощью /prompt-update.
В общем, приятной эксплуатации. За обновлениями — в мой канал.
ссылка на оригинал статьи https://habr.com/ru/articles/1023998/