Ревью стало узким местом: мой протокол для PR от AI-агентов
Агенты пишут больше кода, чем команда способна прочитать. Разбираю конкретный протокол ревью: лимит на диф, тиры по радиусу поражения, детект правок тестов и 12-минутное чтение, которое реально ловит баги.
Pavel Duglas
AI Automation & MVP Architect
Писать код перестало быть бутылочным горлышком где-то год назад. За обычную неделю я выдаю в три-пять раз больше диффа, чем в 2023-м, и это не повод для гордости - это проблема. Потому что пропускная способность одного человеческого мозга на ревью не выросла в пять раз. Мозг тот же, работает на вьетнамском кофе со льдом и 40 минутах внимания за раз.
Эту поломку я вижу в каждой команде, которая всерьёз посадила агентов писать код: очередь просто переехала. Раньше было “нужны руки, чтобы построить”. Теперь - “у нас 14 открытых PR, половина трогает биллинг, и никто их по-настоящему не читал”. Мержить непрочитанный код агента - это самый быстрый способ получить инцидент в проде, который в команде никто не может отладить: никто это не писал и никто это не читал.
Поэтому я собрал протокол. Не философию, а набор механических правил, которые вшиты в репозиторий и в CI.
Правило 1: лимит на диф, и лимит проверяет машина
Агент с радостью принесёт вам PR на 2400 строк, где он переименовал три сущности, отрефакторил сервис, добавил фичу и “попутно почистил” конфиг. Такое не ревьюят. Такое пролистывают и апрувят.
Мои жёсткие рамки:
- 400 изменённых строк на PR, не считая лок-файлов, генерённого кода и снапшотов
- один смысл на PR: фича, рефакторинг или бамп зависимостей - но не всё вместе
- никакого попутного форматирования в PR с логикой
Лимит живёт в CI, а не в вики, которую никто не открывает:
# .github/workflows/pr-size.yml (ключевой шаг)
CHANGED=$(git diff --numstat origin/main...HEAD \
-- . ':(exclude)*.lock' ':(exclude)**/generated/**' ':(exclude)**/__snapshots__/**' \
| awk '{a+=$1; d+=$2} END {print a+d}')
if [ "$CHANGED" -gt 400 ]; then
echo "В PR $CHANGED строк. Разбей или поставь лейбл 'oversized-approved'."
exit 1
fi
Лейбл-предохранитель обязателен. Большие механические миграции бывают. Но по умолчанию должно быть трение, иначе естественный размер вывода агента побеждает всегда.
Практическое следствие: я теперь сообщаю агенту ограничение заранее. “Реализуй только слой репозитория для этой фичи. HTTP-хендлеры не трогай. Цель - меньше 300 строк.” На том же усилии получаешь и код лучше, и диф, который реально можно прочитать.
Правило 2: ревьюить план, а не диф
Самое дешёвое ревью происходит до того, как появился код. Для всего нетривиального я заставляю агента сначала выдать файл плана: какие файлы он тронет, какие функции добавит, что читает и что пишет, какие миграции, и какие сценарии отказа он сознательно не покрывает.
Я читаю это три минуты. Девяносто процентов плохих PR умирают здесь, потому что неправильный план виден сразу, а неправильный код - нет. “Ты собираешься добавить таблицу users_meta” - нет, у нас для этого уже есть JSONB-колонка. “Ты вызываешь платёжного провайдера внутри хендлера запроса” - нет, это уходит в очередь задач.
После того как план согласован, ревью диффа превращается из следствия в сверку. Я не спрашиваю “что этот код делает?”, я спрашиваю “это то, о чём мы договорились?”. Это принципиально более быстрая операция для головы.
Правило 3: тиры по радиусу поражения, а не по числу строк
Изменение на 600 строк в лендинге опаснее, чем правка на 8 строк в функции обновления токена? Нет, наоборот. Поэтому я перестал сортировать PR по размеру и начал сортировать по тому, что сломается, если код неверный.
Карта лежит прямо в репозитории:
# review-tiers.yml
tier_1_читать_каждую_строку:
- src/billing/**
- src/auth/**
- migrations/**
- infra/**
- src/**/webhooks/**
tier_2_читать_логику:
- src/api/**
- src/jobs/**
tier_3_доверять_тестам:
- src/ui/**
- content/**
- scripts/one-off/**
Tier 1: читаю каждую строку, проговариваю про себя, запускаю локально. Ни один агент не мержит tier 1 без человека, который сможет объяснить этот код через месяц.
Tier 2: читаю логику и ветки ошибок, остальное просматриваю, дальше полагаюсь на интеграционные тесты.
Tier 3: если CI зелёный и preview-деплой выглядит нормально - в мерж.
Именно это изменение вернуло мне больше всего ресурса на ревью. Основная масса вывода агента - это tier 3. Пытаться читать его с строгостью tier 1 значит сжигать внимание, которое нужно на код, реально способный стоить денег.
Правило 4: тесты - это спека, а правки тестов подозрительны
Самое опасное, что делает агент, - это делает сьют зелёным, меняя сам сьют. Я видел, как агент “починил” падающий ассерт, ослабив его с конкретного значения до expect.anything(). Формально зелено. Фактически ложь.
Два контрмеры.
Первая: для всего в tier 1 и tier 2 тест пишу я, до того как агент напишет реализацию. Не религия TDD, а два-три ассерта, которые кодируют бизнес-правило. “Возврат больше суммы исходного платежа должен бросать исключение.” Этот тест - мой контракт, он лежит в файле, который агенту запрещено менять инструкцией.
Вторая: CI отдельно подсвечивает правки тестовых файлов, чтобы они не проскочили внутри большого диффа:
TEST_DELTA=$(git diff --numstat origin/main...HEAD -- '**/*.test.ts' | awk '{d+=$2} END {print d+0}')
if [ "$TEST_DELTA" -gt 0 ]; then
echo "::warning::В этом PR удалено $TEST_DELTA строк тестов. Нужен ручной сайн-офф."
fi
Сигнал - именно удалённые строки тестов. Добавленные тесты почти всегда норм. Ловушка живёт в снятых ассертах. Каждую удалённую строку теста я читаю всегда, независимо от тира.
Правило 5: человеческое внимание не тратится на то, что проверяет машина
Если я поймал себя на комментарии про форматирование, нейминг, порядок импортов, отсутствующие типы, необработанный промис или забытый console.log - я слил слот в рабочей памяти. Всё это место тулинга, а не человека.
Минимальные ворота до того, как PR увидит человек:
- форматтер и линтер, причём в CI автофикс выключен: пусть падает, а не молча перезаписывает
- строгая типизация, никаких
anyв путях tier 1 и tier 2 - юнит плюс интеграционные тесты, с живой базой, а не с моком
- диф зависимостей: любой новый пакет в лок-файле требует одной строки обоснования в описании PR
- скан на секреты, потому что агенты обожают “временно” заинлайнить ключ
Последний пункт - не паранойя. Инстинкт кодового агента, когда чтение конфига падает, - захардкодить значение, при котором тест проходит.
Правило 6: 12-минутное чтение
Когда PR доходит до меня, я делаю проход в фиксированном порядке и засекаю время. Если не уложился примерно в 12 минут - PR слишком большой и уезжает на разбиение.
- Читаю описание PR и сверяю с согласованным планом. Расхождение - стоп.
- Читаю изменения слоя данных: миграции, запросы, схемы. Здесь живёт необратимый ущерб.
- Читаю ветки ошибок. Агенты пишут прекрасный happy path и забывают, что существует сеть. Что будет на таймауте, на 429, на частичной записи?
- Читаю каждую удалённую строку. Удаления - это место, где поведение исчезает бесшумно.
- Гребу грепом по границам системы: новые сетевые вызовы, новые записи, новые env-переменные, новые фоновые задачи.
- Запускаю. Реально нажимаю кнопку. Две минуты пользования фичей выигрывают у двадцати минут чтения.
Обратите внимание: построчное чтение новой бизнес-логики - не первый шаг. Это как раз то, что лучше всего покрывают типы и тесты.
Метрики, которые показывают, что протокол работает
Я смотрю четыре числа в месяц, и все они дёшево достаются из Git и из журнала инцидентов:
- медианный размер PR в читаемых строках - должен ползти вниз
- медианное время от открытия до мержа - должно ползти вниз
- доля реверта и доля пост-мерж хотфиксов - настоящий сигнал качества
- доля смерженных PR, где ни один человек не оставил содержательного комментария - если она выше примерно трети, вы штампуете апрувы
Последняя метрика самая честная. Скорость апрувов выглядит как продуктивность ровно до той недели, когда вы три дня отлаживаете систему, которую в комнате не понимает никто.
Версия для соло-фаундера
Если вы один человек и катите MVP, вам не нужны CODEOWNERS и график дежурств по ревью. Нужны три вещи:
- Файл плана на каждую фичу, который вы действительно читаете до того, как агент начал кодить.
- Список tier 1 из пяти-десяти файлов, которые агент не трогает без вашего чтения. Обычно это auth, платежи, миграции, обработчик вебхуков, конфиг деплоя.
- Зелёный CI, в котором есть хотя бы один интеграционный тест, бьющий в настоящую базу.
Всё остальное можно отдать тестам и роллбэкам. Смысл не в том, чтобы ревьюить больше. Смысл в том, чтобы заранее решить, где ваше внимание дороже вашего тулинга, и потом защищать это внимание жёстко.
Команды, у которых сейчас болит, страдают не от того, что агенты пишут плохой код. Код агентов в основном нормальный. Они страдают потому, что генерацию масштабировали в пять раз, а ревью оставили как было, и потом удивились, что очередь встала. Сначала починить конвейер ревью. Генерация уже и так работает.
Вопросы и ответы
Не проще ли просто просить агента писать меньше кода, вместо всех этих правил в CI?
Просить нужно, и я прошу: ограничение по размеру и по области ответственности идёт в prompt заранее. Но просьба не воспроизводима. В следующей сессии, с другим контекстом или другой моделью, агент опять принесёт 2000 строк, потому что ему так удобнее закрыть задачу. Правило в CI работает всегда и без вашего участия, а prompt - только пока вы о нём помните. Держите оба уровня: prompt задаёт норму, CI её удерживает.
Как понять, что агент подкрутил тесты, а не починил код?
Смотрите на удалённые и ослабленные строки в тестовых файлах, а не на добавленные. Три типовых признака: точный ассерт заменён на проверку "что-нибудь пришло", тест помечен skip или todo, ожидаемое значение переписано под фактический вывод кода. Я отдельным шагом CI считаю число удалённых строк в файлах тестов и требую ручного сайн-оффа, если оно больше нуля. Плюс критичные бизнес-правила я оформляю тестами сам, до реализации, и запрещаю агенту редактировать эти файлы.
Сколько времени реально уходит на такой протокол и не убивает ли он скорость MVP?
Наоборот. Три минуты на чтение плана и до 12 минут на проход по PR - это меньше, чем один вечер отладки чужого непрочитанного кода в проде. На MVP я обычно урезаю протокол до трёх элементов: план перед кодом, список из пяти-десяти файлов tier 1, которые не мержатся без чтения, и один интеграционный тест на живой базе в CI. Это добавляет минуты к каждой фиче и снимает основной класс дорогих ошибок: деньги, доступы, необратимые миграции.
Похожие статьи
Сделаю под ключ
Доведу вайб-кодинг-прототип до рабочего продукта
Разберу, что нагенерировал ИИ, закрою дыры в безопасности и данных и выложу в прод.
от 1 500 $ · 1-2 недели