Содержание
- Агенты по расписанию бесполезны на моей боевой кодовой базе
- Старый цикл против нового
- Настоящий враг: техдолг, которого не видно
- Твой CLAUDE.md — это пожелание. Твой линтер — нет.
- Guardrails: главное ограничение — это сложность
- Что на самом деле ловит CI
- Инвестиция
- Организм
- С чего начать
- Заключение
- Присоединяйтесь к обсуждению
Агенты по расписанию бесполезны на моей боевой кодовой базе
В Claude Code на днях завезли агентов по расписанию. Повторяющиеся задачи — нативно, прямо из коробки. То самое, о чём мы мечтали с первых демок AI-кодинга: ставишь агента на ночь, ложишься спать, а наутро PR'ы уже замёржены. Инженер, который работает, пока ты спишь.
А я сижу и думаю: да я ведь даже воспользоваться этим не могу. Ни на боевой кодовой базе — то есть у себя на работе. Я годами работал в archive.com, и за это время мы сменили три дизайн-системы: начали с Shopify Polaris, переехали на Ant Design, когда переросли Shopify, а потом на shadcn/ui и Tailwind — потому что и Ant Design к тому моменту сам оброс легаси. Годы работы, три UI-фреймворка, куча договорённостей, которые жили только в головах, и бизнес-правила, которые никто никогда не записывал. Натрави на это агента — он рванёт вперёд. Выдаст код. Красивый, идиоматичный, дьявольский код, который тащит импорты из всех трёх дизайн-систем в один файл и каким-то чудом проходит все твои проверки.
И что с этим делать? Ревьюить всё подряд нереально. Тормозить агентов нельзя. И уж точно нельзя надеяться, что они сами сообразят, какую дизайн-систему тут использовать.
Вот к чему я в итоге пришёл. Я работаю в Claude Code, поэтому и примеры все оттуда, но по большому счёту разницы нет — хоть Cursor, хоть Copilot, хоть Codex, хоть Devin.
Старый цикл против нового
Старый цикл: пишешь или ревьюишь код, на чутьё ловишь код-смеллы, оставляешь комментарии с объяснением, что имелось в виду, обещаешь поправить «потом» (что обычно означало «никогда»).
Новый цикл: один раз описываешь правила кодом, даёшь агентам биться о них лбом, смотришь, что отваливается, подтягиваешь ограничения. Меньше «запомни на будущее» — больше «такое в принципе не может случиться».
Мне хватило недели, чтобы это прочувствовать. Когда код можно генерировать без остановки, узкое место — это ты сам. Не агент. Ты.
Настоящий враг: техдолг, которого не видно
Вот о чём тебя никто не предупреждает: страшнее всего не когда агенты ломают код. Страшнее всего — когда не ломают. Код компилируется, проходит все тесты, на ревью выглядит безупречно — и при этом тихо нарушает те архитектурные допущения, которые ты считал незыблемыми.
Я видел это своими глазами. Агент добавляет форму на страницу, которую я уже перевёл на shadcn/ui. И берёт <Form.Item> из Ant Design — потому что соседняя форма на той же странице всё ещё на нём. Компилируется. Рендерится. Моя миграция только что откатилась на один компонент назад — и пайплайн этого даже не заметил.
С CSS та же беда. Агент пишет новый компонент на Tailwind-утилитах, всё по нашему текущему стандарту. Но значение паддинга копирует из старого Ant Design-компонента по соседству: p-[24px] вместо нашей шкалы p-6. Одно магическое число тебя не убьёт. Пятьдесят — убьют. Каждый коммит по отдельности выглядел нормально. А спохватываешься, когда уже три недели как тонешь в рассинхроне.
Человек бы это поймал. Взглянул бы на импорт и сказал: «стоп, а зачем мы вообще тащим сюда Ant Design?» У агента такого чутья нет. А без жёстких сигналов ты в пятнадцатый раз пишешь «всё ещё не работает».
Источник: ProgrammerHumor.io
Вот тогда я и зациклился на циклах обратной связи. Насколько быстро агент может узнать, что ошибся?
Твой CLAUDE.md — это пожелание. Твой линтер — нет.
Сначала я среагировал ровно как все: надо писать инструкции получше. CLAUDE.md. Библиотеки скиллов. Аннотации в документации. Опишешь всё достаточно чётко — и агент послушается, правда же? Vercel вон даже выкатил библиотеку скиллов — 40+ правил по производительности React, аккуратно написанных, оформленных как файлы SKILL.md для AI-агентов. Реально толковая штука.
Но на одних инструкциях далеко не уедешь. Особенно когда агент выдаёт больше кода, чем человек физически способен отревьюить.

Пойми меня правильно: инструкции важны. Хороший CLAUDE.md заметно сокращает число итераций. Но между «помогает» и «гарантирует» — пропасть, и CLAUDE.md прочно сидит на стороне пиратского кодекса: это рекомендации, а не правила. По сути мы ставим всю кодовую базу на то, что вероятностная система будет попадать в цель каждый раз. Иногда так и есть. Иногда с первой же попытки получаешь красивый идиоматичный код. А иногда она роняет неиндексированный запрос в горячий путь — и узнаёшь ты об этом, когда база ложится в два часа ночи в пятницу.
И тут до меня дошло: мы же уже решали эту проблему. Десятилетия назад. Юнит-тесты появились, потому что люди забывают про граничные случаи. Линтеры — потому что люди не могут договориться, куда ставить фигурную скобку. CI — потому что «у меня на машине работает» перестало быть смешным после третьего падения прода. Всё это известно сто лет.
И всё равно с LLM мы наступаем на те же грабли. «Просто напиши очень хороший CLAUDE.md». «Просто добавь побольше скиллов». Поразительно, как быстро всё забылось.
CLAUDE.md объясняет «почему» и помогает агенту попасть с первого раза. Lint-правило гарантирует, что он не промахнётся. Скиллы дают скорость. Линтеры держат в рамках. Если выбирать что-то одно — бери линтер.
Guardrails: главное ограничение — это сложность
Вот что у меня прогоняется на каждое изменение:
- ESLint — потому что у агента нет десяти лет мышечной памяти на твои соглашения об импортах
- SonarJS — чтобы рубить целые классы багов на корню
- строгий TypeScript — если типы разболтаны, агент пролезет в любую щель
- жёсткие правила по React — чтобы в три часа ночи не рождались «креативные» паттерны компонентов
- Prettier — потому что споры о форматировании закрыты
Но честно? Самое мощное, что я сделал — а перепробовал я много чего, — это зверски строгие лимиты на сложность. Не правила стиля. Именно жёсткие потолки на то, насколько большим и запутанным коду позволено становиться.
Оказалось, в ESLint для этого уже всё есть. complexity режет цикломатическую сложность. max-depth ограничивает вложенность. max-lines-per-function вынуждает дробить код. max-params держит интерфейсы узкими. max-statements не даёт функции делать двенадцать дел за раз. SonarJS добавляет cognitive-complexity, которая умнее обходится с вложенными условиями. Жаль, что я не включил всё это сто лет назад.
Дошёл я до этого на собственном опыте — и опыт был весёлый. Без лимитов на сложность агент бодро выдаст тебе одну функцию на 150 строк: шесть уровней вложенности, три ранних return-а и switch внутри try-catch внутри цикла. Компилируется. Тесты проходят. Даже работает. Потом следующий агент её трогает, делает ещё хуже — и поздравляю, теперь у тебя две функции по 150 строк. Поставь max-lines-per-function: 40, complexity: 10, max-depth: 3, добавь --max-warnings=0 — и смотри, что начнётся. Агенту просто придётся всё разбить. Он начнёт выделять хелперы, нормально называть сущности, разносить ответственность. Будто всё это время умел писать чистый код — просто нужно было настоять. Конкретные числа важны меньше, чем сам факт лимитов. Начинай со строгих значений и ослабляй только тогда, когда они реально начинают мешать.
Я всё время говорю про ESLint, потому что это моя территория, но стек тут по большому счёту не важен. RuboCop, Ruff, clippy — принцип тот же. Готовые линтеры закрывают синтаксис. А вот под твою архитектуру понадобится что-то своё.
Что меня удивило: ещё до того, как писать что-то своё, я здорово выиграл просто за счёт плагинов, до которых у нас годами не доходили руки. SonarJS, unicorn, perfectionist. Они существовали давным-давно — мы их просто так и не подключили. Обычная отмазка была: «там слишком много нарушений, не разгребём». С агентом эта отмазка не работает. Натравливаешь его на нарушения — и он разгребает их пачками. Пять минут, конфиг recommended — и целые классы багов исчезают.
А это можно сделать lint-правилом?
В какой-то момент я поймал себя на одном вопросе: а это можно сделать lint-правилом? И так — каждый раз, когда что-то шло не так. Дошло до автоматизма. И как только этот переключатель в голове щёлкает, ты больше не ревьюер. Ты тот, кто следит, чтобы ошибка не повторилась. Никогда.
Первое правило даётся тяжелее всего. Дальше — как снежный ком. Реальный пример: наши агенты с завидным упорством совали console.log в продакшн вместо нашего логгера, который шлёт всё в Datadog. Каждый. Божий. Раз. Lint-правило на десять строк это закрыло. Запретил console.log, а в подсказке предложил logger.error. Всё. Больше я об этом не думал.
Люди слышат «50 кастомных правил» и хватаются за голову. Понимаю, сам так делал. Часть этих правил и правда окажется неудачной. Но вот что происходит дальше: кто-то налетает на плохое правило, бесится и открывает PR, чтобы его поменять. И внезапно у вас идёт тот самый разговор об архитектуре, которого раньше просто не было. Этот PR ценнее самого правила. Кодовая база с плохими правилами — это состояние, которое можно улучшать. Кодовая база без правил — это просто вайбы. А когда правило требует переписать существующий код, AI вместе с кодмодами превращают эту чистку из квартальной эпопеи в пару часов работы.
Линтинг — это одна половина. Тесты — вторая, и тут AI сделал их до неприличия дешёвыми. Те самые дотошные, исчерпывающие тест-сьюты, до которых я всё обещал себе «когда-нибудь добраться»? Агент выбивает их за минуты.
Одна беда: правила ловят только то, что ты уже видел. А всё, что тебя ещё не укусило, по-прежнему где-то поджидает.
Что на самом деле ловит CI
Каждый пуш запускает полную проверку. Ничего личного к агенту — я не доверяю ничему, что не было проверено. В том числе и собственному коду, если уж на то пошло.
Скриншот-тесты на Playwright стали для меня переломным моментом. То, что они вылавливают, поражает: регрессия z-index, из-за которой модалка прячется под оверлеем; сдвиг вёрстки после рефакторинга flex-контейнера; кнопка, которая отрисовалась, но абсолютно некликабельна. Ничего из этого юнит-тесты не видят. Chromatic делает то же самое для процессов вокруг Storybook. Если на экран никто не смотрит — экран рано или поздно сломается; эту истину я в своё время усвоил на горьком опыте.
Property-based тестирование я годами обходил стороной. Вместо отдельных тест-кейсов ты описываешь свойство, которое должно выполняться всегда: «эта функция никогда не возвращает отрицательное число» или «закодировал и декодировал — получи ровно то, что было на входе». Фреймворк генерирует сотни случайных входов и пытается это сломать. Невероятно эффективно — но я так и не внедрил, потому что писать хорошие описания свойств было занудно. AI это перевернул. Теперь я просто натравливаю агента на код, и он сам соображает, какие свойства должны выполняться. Одна команда прогнала агентов по 933 модулям и получила 984 баг-репорта, из них 56% — настоящие. Это примерно $10 за каждый реальный найденный баг.
Безопасность — вот что меня по-настоящему отрезвило. DryRun Security прогнали Claude Code, Codex и Gemini на сборке двух приложений — и в 87% PR была хотя бы одна уязвимость. Не опечатки. Структурные дыры: WebSocket-эндпоинты без аутентификации там, где у REST API она была; rate-limiting-мидлвара, описанная в файле, но так и не подключённая. Саму мидлвару агент написал правильно. Он просто не знал, что она не работает. Вот это меня и зацепило: статический анализ видит, что файл есть, но не понимает, что он никуда не подключён. Нужен CI, который реально поднимает приложение и проверяет поведение.
И сверху — мониторинг в рантайме. Мы завели Sentry и Datadog на очередь задач. Что-то падает в два часа ночи — и это превращается в задачу, которую подхватывает агент. Я просыпаюсь к готовому фиксу, а не к пожару.
Да, обвязки тут много. И менеджер резонно спросил бы: а во сколько всё это обходится?
Инвестиция
Токены — копейки. SaaS-подписки — копейки. Настоящая цена — это твоё время, а фич всё это не приносит. Знаю. Я сам с собой об этом неделями спорил.
Источник: Memedroid
А потом я увидел цифры. CodeRabbit разобрал 470 GitHub-PR и выяснил: в коде, сгенерированном AI, багов в 1,7 раза больше, чем в написанном людьми. Уязвимостей — в 2,74 раза больше. Дословно: «У нас больше нет проблемы с созданием кода. У нас проблема с доверием к нему».
Да, ты платишь за токены. Но таксисты платят за бензин, машину, лицензию, страховку — и всё это на марже в 5–10%, и никто не возмущается. В софте мы немного зажрались: валовая маржа 80%+, а все средства производства — это ноутбук и стул, которые у тебя и так есть. Тулза за $200 в месяц, которая ловит хотя бы один прод-баг в квартал? Чувак, да она уже десять раз себя окупила.
Senior-инженер обходится компании в $150–200 в час со всеми накладными. А прод-баг, который нашёл клиент? Это дни на разбор, экстренные фиксы и подорванное доверие, которое уже не вернёшь. Кастомное же lint-правило пишется за полдня и ловит этот класс багов навсегда. Скриншот-сьют на Playwright поднимается за день. Не понимаю, чего я так долго тянул.
И эффект накапливается. Каждый guardrail умножает то, что агент может выкатить без тебя. Ещё одно правило — на одну заботу меньше на ревью. Десять правил — и целые категории багов просто… перестают появляться.
Карпати сказал это лучше, чем смог бы я: «Цель — забрать себе весь рычаг, который дают агенты, и при этом ничуть не поступиться качеством софта». Собственно, к этому я всю статью на ощупь и продвигался.
Организм
Когда я наконец собрал всё это в единую систему, что-то щёлкнуло. Дальше она начала затягиваться сама собой:
┌─────────────────────────────────────────┐
│ │
▼ │
Agent ──▶ Rules ──▶ CI ──▶ Observability │
│ │
▼ │
Tasks ────────┘Вот как теперь выглядит обычный вторник: агент открывает PR. Кастомное lint-правило ловит нарушение в barrel-файлах — агент чинит. CI прогоняет Playwright, скриншот показывает сдвиг вёрстки — агент правит CSS. Sentry сообщает о всплеске 404-х на стейджинге — заводится задача, агент её подхватывает. Я за кофе просмотрел пару диффов. Больше ни строчки кода никто руками не написал.
Каждый баг, доехавший до CI, превращается в правило, которое предотвращает следующий. Система питается собственными сбоями. В какой-то момент я перестал называть это тулчейном — это больше похоже на организм.
И я такой не один. Spotify построил фонового кодинг-агента Honk поверх инфраструктуры обратной связи, в которую вкладывался ещё с 2022 года — за три года до всей этой истории с AI. Сейчас они вливают в продакшн больше 650 агентских PR в месяц. У Devin процент мёржа удвоился — с 34% до 67%, когда подтянули понимание кодовой базы. Не модель — контекст вокруг неё.
История каждый раз одна и та же. Модель не стала умнее. Цикл стал плотнее.
С чего начать
Где ты сейчас?
| Уровень | Как это выглядит | Признак |
|---|---|---|
| 0 — Vibes | Никакого кастомного линтинга, никакого CI, всё ревьюишь руками | «Между агентом и продом — только мои глаза» |
| 1 — Guardrails | Стандартные линтеры и CI, но без кастомных правил | «Агент проходит линт, но по архитектуре всё равно уплывает» |
| 2 — Architecture as Code | Кастомные lint-правила, в которых зашиты соглашения команды | «Правила из CLAUDE.md переезжают в линтер» |
| 3 — The Organism | Самозатягивающийся цикл: агент → правила → CI → observability → задачи → агент | «Я ставлю агентов на ночь и утром смотрю диффы» |
Если ты на нулевом — я был там же год назад. Вот что мне самому хотелось бы тогда услышать:
Сегодня: тот комментарий к PR, который твоя команда оставляет снова и снова — про соглашения об импортах, barrel-файлы, console.log в проде, — преврати в lint-правило. Всего одно. С этого всё и начинается.
На этой неделе: добавь скриншот-тесты на Playwright для трёх самых важных страниц. Я сам обалдел от того, сколько всего они ловят — и сколько при этом упускают юнит-тесты.
В этом месяце: поставь агента на что-нибудь безопасное. Обновление зависимостей, поддержка тест-сьюта, чистка протухших веток. Пусть пашет ночью, а ты с утра смотришь PR. Я начинал с Claude Code в браузере; когда этого стало мало, дешёвый VPS дал больше мощности под ту же идею.
Как понять, что всё работает: ты можешь поставить задачу с телефона, глянуть дифф по дороге и довериться результату. Не то чтобы ты стал работать меньше — просто больше не привязан к ноутбуку.
Если нужна отправная точка — я собрал плейбук с наборами инструментов под разные бюджеты и репозиторий-компаньон vigiles, который часть всего этого автоматизирует. Сэкономил бы себе пару выходных, будь у меня такое тогда.
Заключение
Три дизайн-системы за пять лет. Агент не знает, какую из них использовать, пока ты ему не скажешь — детерминированно, на каждом коммите.
Вот, собственно, и всё. Тот самый агент, которого я не мог натравить на свою кодовую базу? Он ждал не модели поумнее. Он ждал датчиков получше.
LLM-ки вероятностны. В большинстве случаев они угадают — но на реальной кодовой базе «в большинстве случаев» рано или поздно угробит тебе пятницу. Я с этим смирился. Никакой prompt engineering этого не лечит.
Поэтому я перестал гоняться за хитрыми промптами. И начал гоняться за скучной, детерминированной, занудной обратной связью. За той, что срабатывает, смотрю я на неё или нет. За той, которой плевать, насколько уверена была модель.
Линтеры не спят, а CI не устаёт. Про себя я такого сказать не могу.
Заголовок — отсылка к «Attention Is All You Need» (Vaswani et al., 2017), статье, представившей архитектуру Transformer.