Всё началось с прокрастинации. Готовился к собеседованиям: SQL, git, брокеры сообщений, Django, алгоритмы. Открывал статьи, читал полтора абзаца и через десять минут обнаруживал себя где угодно, только не за материалом. К третьему заходу стало ясно: в формате «читать и запоминать» это в меня не лезет.

Дальше сработала классика прокрастинации: не хочется решать задачу, строй инструмент для неё. Не хочется зубрить SQL, значит нужен тренажёр для SQL. Короткая теория, задачи прямо в браузере, повторение по расписанию. Вместо подготовки к собеседованиям я строил платформу для подготовки к собеседованиям.

Спойлер: план провалился, учиться всё равно пришлось. Но по дороге вышло кое‑что интереснее самого тренажёра, про это и статья.

Код в основном писал Claude Code, я задавал направление и проходил через несколько итераций пока не достиг удовлетворяющего результата. Про сам вайбкодинг как явление писать не буду: если вы читаете статью про вайбкодинг на Хабре в 2026 году, вы и так знаете, что это такое. Разница между вайбкодингом на коленке и вайбкодингом, который выкатывается в прод с чужим кодом в браузере у посетителя, не в модели и не в качестве промпта. Она в инженерии вокруг агента: что происходит до первого запроса, какие процессы воспроизводимы, а какие получились разовой удачей, и когда нужен не один агент, а десяток параллельно. Дальше об этом, на конкретном примере.

анимация: архитектура проекта
анимация: архитектура проекта

Что я в итоге собрал
Сначала о том, что за проект, чтобы дальше был понятен масштаб.
.learn, тренажёр для подготовки к техническим собеседованиям, работает по принципу local‑first: весь прогресс, пройденные темы и карточки на повторение, хранится в IndexedDB на клиенте, а не на сервере. Деплой отдельного сайта тут опция, а не обязательное условие; демо‑сайт просто показывает, как это выглядит, если поднять у себя.
39 тем и 219 концептов на двух языках. От основ SQL до устройства LLM. Каждая тема включает теорию с визуализациями (больше пятидесяти схем), задачи и карточки для повторения по FSRS, плюс упражнения семи типов. От тестов до git‑челленджа.

Исполнение кода.

анимация: выполнение SQL кода
анимация: выполнение SQL кода


Python гоняется прямо в браузере на Pyodide, там же пошаговый трассировщик переменных.
SQL выполняется в настоящей SQLite через sql.js.
Для git‑задач написали детерминированный движок: коммит, ветка, мердж, проверка инвариантов вроде «текущая ветка это main» или «нет несохранённых изменений».
Всё крутится в отдельном Web Worker, из которого явно вырезаны fetch, XMLHttpRequest, WebSocket и обмен между вкладками. Подробности ниже, в разделе про песочницу.
Опциональный бэкенд на NestJS обслуживает только публичный каталог заявок на темы и админку с 2FA. Без него плеер работает так же, просто без формы «предложить тему».

В рантайме нет ни одного обращения к LLM. Модель работала только на этапе производства контента и кода. Готовый сайт живёт как чистая логика: офлайн, без ключей, без подписок.

Про цифры
раз уж заговорил о масштабе: около 237 тысяч строк кода. В основном это учебный контент (уроки, задачи, разборы для собеседований), а не бизнес‑логика. Само приложение, то есть веб‑клиент, бэкенд и общие пакеты, это ~60 тысяч строк.

Время разработки
Рабочий каркас (плеер, песочница, первые темы, публичный каталог заявок) собрался за пару дней почти без отрыва от терминала. Дальше пошли уже недели, и ушли они не на фичи, а на то, что в демках обычно пропускают: контент на двух языках, тесты и несколько отдельных проходов по безопасности перед тем как показывать это кому‑то кроме себя. Вторая половина статьи как раз про эту часть, потому что она интереснее MVP.

Главный тезис
Агент это толковый исполнитель без контекста продукта, а не оракул. Без правил, чек‑листов и гейтов он быстро сделает не то, что вы имели в виду, а буквально то, что сказали. Дальше не про то, как выпросить у модели код, а про то, чем это отличие закрывается на практике: скиллы вместо разовых промптов, ревью, автоматические гейты, оркестрация нескольких агентов сразу.

Скиллы вместо разового промпта
Первые темы генерировал обычными запросами в чат. Работало, но качество плавало: то теория без единого примера, то структура файлов чуть съезжает и загрузчик темы её не принимает. К третьей теме поймал себя на том, что второй раз за вечер вставляю в чат один и тот же промпт на полстраницы, и снова часть из него забыл. Процессу нужен файл, а не моя память к полуночи.

Скилл называется lesson‑forge, в нём 176 строк.
Девять шагов: разобраться в теме, свериться на пересечения с уже готовыми, план, каркас файлов, теория и задачи, прогон проверки, вычитка глазами ученика, сохранение. Один шаг с жёсткой остановкой: план темы агент показывает на утверждение и без апрува не идёт дальше. Минута на план против вечера на переделку готовой темы, вот и вся цена остановки.

Рядом лежат справочники, которые агент открывает по требованию: каталог из семи типов упражнений, гайд по визуальным компонентам, стиль текста. И десять гейтов качества, от «манифест темы валиден по схеме» до «в каждом разделе есть хотя бы одна иллюстрация, и тема читается на экране 375px». Похоже на перебор для генератора уроков, но каждый гейт появился после конкретного брака на проде, так что не жалею.

анимация: как работает скилл
анимация: как работает скилл


Больше всего окупился один конкретный гейт: эталонное решение каждой задачи реально выполняется и сверяется с ожидаемым результатом. Для SQL и git это честно прогоняется через sql.js и собственный git‑движок. Если решение, придуманное агентом, само не проходит собственную задачу, тема не проходит гейт. Такие баги не ловятся глазами: на вид всё правдоподобно.

Оговорка была прямо в тексте гейта, чёрным по белому: Python и JavaScript автоматика не прогоняет вообще, для них нужна ручная проверка в браузере. Написано в чек‑листе не значит выполняется. К чему это привело, расскажу ниже, в разделе про поломки.

анимация: пайплайн lesson-forge
анимация: пайплайн lesson‑forge

Деталь, о которой редко пишут
скиллы хранятся в двух копиях, канон для Cursor и зеркало для Claude Code, и со временем расходятся. Лечится скучно, но надёжно: команда синхронизации плюс CI‑гейт, который валит сборку при рассинхроне. Разошлись, сборка красная, идёшь чинить.

Когда агентов много
Один агент это один исполнитель, но их можно запускать пачкой. Пять новых тем как‑то сгенерировал семью помощниками параллельно: по одному на тему, самую объёмную разбил на двоих. Каждый работает в своём отдельном чате, в основной чат возвращается только короткий итог. Два правила из этого запуска.

Первое:
большие пачки запускать только в фоне. Первый раз запустил синхронно, и вся пачка легла на середине из‑за одной сетевой ошибки, утащив всё написанное. В фоне упавший перезапускается сам, а остальные даже не замечают.

Второе:
результат пачки проверяется запуском, не чтением. Прогнал почти три сотни примеров predict‑output через настоящий интерпретатор. Совпало всё, но заодно всплыл старый баг в задаче недельной давности: код когда‑то поправили, а ожидаемый ответ обновить забыли. Чужие ошибки агенты ловят хорошо, свои почти никогда.

Распределение ролей
глубокий анализ уходит к более сильной и медленной модели, механика к быстрой, а главный агент только дирижирует и не тратит свой контекст на подробности.

Сильнее всего на качество повлияли опровергатели. В security‑проверках помощники работают в два слоя: первый ищет проблемы, второй получает каждую находку с единственной задачей опровергнуть её. Выживает то, что опровергнуть не удалось; находки уровня critical и high проваливают проверку без исключений. Без второго слоя отчёт тонет в убедительно звучащих ложных срабатываниях.

CLAUDE.md
Всё держится на файлах, а не на моей памяти. Тут два уровня: профильный CLAUDE.md общий на все мои проекты, и короткий проектный. В .learn проектный файл по сути «сначала прочитай AGENTS.md, это источник правды по сборке и конвенциям» плюс десяток строк триггеров: добавить тему это lesson‑forge, обновить README это generate‑readme, руками не трогать ни то, ни другое. AGENTS.md никогда не редактируется руками, только через скилл, который пересчитывает его из кода вместе с бейджем строк в README. У меня один стандарт документации на все проекты, называю его DotCore.

Вот смысловые куски профильного файла как есть:

Неопределённость
Перед каждым ответом оцени уровень неопределённости. Если он выше 0.1, задавай уточняющие вопросы, пока неопределённость не снизится. Не отвечай наугад.

Опасные операции
Перед удалением любого файла запрашивай явное подтверждение.

Никогда не читай и не выводи файлы, которые могут содержать секреты:.env и её варианты,.pem,.key, credentials.json, всё, где в имени или пути есть secret, token, credential, password.

git commit, git push, git reset ‑hard выполняй только с явного подтверждения.

Имена коммитов в стиле Conventional Commits; когда часть работы готова, предложи имя коммита, но не коммить сам.

Стиль текста
иши как человек: без длинных тире, без слов‑маркеров нейросетей, без похвалы в начале ответа и шаблонных вводных.

Оркестрация
Ты оркестратор. Глубокий анализ делегируй более сильной модели, рутину отдавай быстрой. Держи свой контекст чистым.

Неопределённость окупается лучше всех остальных пунктов: пара уточняющих вопросов дешевле, чем большой аккуратный дифф не по адресу. Правило про коммиты продублировано отдельным git‑хуком прямо в DotLearn: commit‑msg вырезает «Co‑Authored‑By: Claude» и «Generated with Claude Code» построчно, если агент вдруг решит их вписать. Под коммитом моё имя, значит я его читал и отвечаю сам.

анимация «Что на самом деле видит модель»

Песочница
Раз платформа выполняет чужой код прямо в браузере у постороннего человека, изоляция сделана с запасом. Пользовательский код выполняется в отдельном Web Worker, из которого сразу после старта вырезаны fetch, XMLHttpRequest, WebSocket и обмен между вкладками. Чистое пространство на каждый запуск, лимит на вывод, принудительный таймаут.

Деплой
Без экзотики, но с одним принципом: сервер ничего не собирает сам. Основной путь без Docker: идемпотентный скрипт ставит Node, пакетный менеджер и веб‑сервер с авто‑HTTPS, поднимает сайт и API как системные сервисы. Второй путь через Docker: образы собираются на CI‑раннере, где памяти с запасом, пушатся в реестр, а сервер их только скачивает и перезапускает, ничего не компилируя на боевой машине. От мержа в main до обновлённого сайта человек не нужен. Скрипты для этого тоже писал агент.

Про модели, если коротко
Одну тему, про устройство LLM, платформа сгенерировала про саму себя, и мне пришлось пройти её как ученику, чтобы проверить адекватность. План оттянуть учёбу дал первую трещину.

Пересказывать трансформеры не буду. Практический вывод один, правила должны быть короче и стоять в начале промпта, а не прятаться в третьем сообщении часового диалога. Про next‑token prediction и то, что уверенная галлюцинация не баг, а нормальный режим работы модели, все и так знают. Вопрос только в том, строите вы вокруг этого проверки или нет.

Что в итоге
Вайбкодинг вышел не про «нейросеть всё сделает сама», а про управление: правила в файле, воспроизводимые процессы вместо разовых промптов, автоматические гейты, ревью руками, слой, который пытается опровергнуть находки. Каждая история из раздела про поломки это дырка в этой системе, и каждую пришлось закрывать новым слоем проверок. Агент от этого не становится непогрешимым, просто его ошибки становятся дешёвыми, а это ровно то, что нужно.

Прокрастинация, как и положено, в итоге провалилась. Материал к собеседованиям выучил на собственном тренажёре, с карточками и повторением по расписанию. Зубрить пришлось ровно то, от чего убегал.

Тренажёр по дороге перерос в отдельный проект. Исходники открыты для чтения, тему можно предложить прямо на сайте. Ссылки ниже, там же остальные мои проекты под тем же стандартом DotCore.

Ссылки
Исходники: https://github.com/network‑user/DotLearn
Сайт: https://learn.dotcore.lol