Вернуться к блогу

Ревью AI-кода сломано. Вот чего не хватает.

Ревью AI-кода сломано. Вот чего не хватает.

Давайте будем честны о том, что большинство из нас не говорит вслух: мы почти не ревьюим код, сгенерированный ИИ.

Diff на 600 строк. Агент затронул 14 файлов. Открываешь pull request, прокручиваешь изменения, щуришься на пару функций и мержишь. Может, сначала запустишь тесты. Может, нет. Код выглядит разумно. Агент сказал, что закончил. Деплоим.

Это не лень. Это структурная проблема. Традиционное code review было спроектировано для мира, где человек писал код и мог объяснить своё рассуждение при вопросе. Где diff-ы были 50-200 строк, потому что столько человек пишет за сфокусированную сессию. Где описание PR говорило "Я выбрал подход X из-за Y" и можно было доверять этому контексту.

AI-агенты работают иначе. Claude Code может произвести 500 строк рабочего кода за две минуты. Описание PR часто сводится к "реализовать фичу X." Diff говорит тебе что изменилось, но ничего о почему. Нет записи о том, какие альтернативы агент рассматривал. Нет объяснения, какие компромиссы он сделал. Нет доказательств, что он действительно что-то тестировал. Ты ревьюишь вывод чёрного ящика, а инструмент ревью показывает только вывод.

Эта статья разбирает, почему ревью на основе diff не работает для вывода агентов, что на самом деле является недостающим слоем, и конкретный паттерн, который делает AI-сгенерированный код проверяемым без утроения времени на ревью.

Разрыв в ревью

Разработчики честны об этом в частных разговорах. В тредах сообщества паттерн повторяется: "Я в основном просто пробегаю глазами по diff-ам агентов." "Проверяю, что тесты проходят, и мержу." "Если выглядит примерно нормально, апрувлю."

Это рациональное поведение при данных ограничениях. Когда коллега-человек отправляет PR, у тебя есть контекст: ты знаешь, над чем он работал, видел тикет, возможно обсуждали подход на стендапе. Diff — это дополнение. Настоящее ревью произошло через общий контекст.

С агентом у тебя ничего этого нет. Агент взял задачу, молчал три минуты и произвёл diff. Единственный контекст — однострочное описание, которое агент оставил в PR. Ревьюить этот diff с нуля, без контекста о намерении или рассуждении, занимает в 5-10 раз больше времени, чем ревью человеческого PR того же размера. Поэтому люди этого не делают. Они выборочно проверяют несколько критических мест и апрувят.

Результат предсказуем. Баги проскакивают, которые были бы пойманы с контекстом. Архитектурный дрифт накапливается, когда агенты принимают мелкие решения, которые складываются. Качество кода деградирует незаметно: не сломано, но и не совсем правильно. Непоследовательная обработка ошибок в одном файле. Запрос к базе данных, который работает, но плохо масштабируется. Новая утилитарная функция, которая дублирует существующую, потому что агент не знал о её существовании.

Ни один из этих сбоев не виден в diff-ревью. Они видны только когда ты понимаешь, что агент пытался сделать, и можешь сравнить его намерение с исполнением.

Почему diff-ов недостаточно

Diff — это запись текстовых изменений. Всё. Для человеческого code review diff-ы работают, потому что ревьюер может вывести намерение из распознавания паттернов и общего контекста. Ты видишь, как коллега добавляет блок try/catch, и знаешь, что он обрабатывает ошибку из баг-репорта прошлой недели. Видишь переименование функции и знаешь, что он следует конвенции именования, о которой команда договорилась.

С кодом, сгенерированным агентом, ты не можешь вывести намерение, потому что не участвовал в процессе рассуждения. Вот что 500-строчный diff агента реально тебе говорит:

  • Какие файлы были изменены
  • Какие строки были добавлены, изменены или удалены
  • Синтаксическая структура нового кода

Вот чего он не говорит:

Почему был выбран именно этот подход. Агент мог рассмотреть три разные реализации. Он выбрал одну. Ты не знаешь почему. Может, выбранная оптимальна. Может, это первое, что он попробовал, и оно работало достаточно хорошо. Из diff-а не узнать.

Какие альтернативы были отброшены. Если агент выбрал стратегию опроса вместо подписок WebSocket, это было обдуманным архитектурным решением или случайностью? Diff не скажет.

Соответствует ли реализация спецификации. Нужно было бы открыть spec в одном окне и diff в другом и вручную сопоставить каждый критерий приёмки. Большинство этого не делает.

Что было протестировано и как. Diff может включать новые файлы тестов. Но агент их запускал? Они прошли? Покрывают ли они крайние случаи из спецификации? Чтобы узнать, нужно чекаутить ветку и запустить их самому.

Остался ли агент в рамках. Может, задача была "исправить баг логина", а агент ещё и отрефакторил middleware авторизации, переименовал две утилитарные функции и обновил схему конфигурации. Все эти изменения выглядят нормально по отдельности. Но они не были запрошены, не были специфицированы и не были протестированы относительно оригинальных критериев приёмки.

Это не проблема конкретного инструмента для diff. Интерфейс ревью GitHub, merge request-ы GitLab, Gerrit, git diff в терминале. Все они показывают одно и то же: что изменилось. Для вывода агента "что изменилось" — наименее важный вопрос. Важный вопрос: делает ли это изменение то, что должно, и ничего больше?

Недостающий слой: нарратив реализации

То, что ревьюерам реально нужно — это след рассуждений агента. Не diff. История реализации: что агент планировал сделать, что реально сделал и как проверил результат. Назовём это нарративом реализации.

Хороший нарратив реализации отвечает на пять вопросов:

  1. Каков был план? До написания кода, что агент намеревался сделать? Какие файлы, какой подход, какой порядок операций?
  2. Что произошло во время реализации? План пережил контакт с кодовой базой? Были ли сюрпризы, повороты или изменения scope?
  3. Каков был итоговый результат? Не diff. Резюме простым языком о том, что изменилось и почему.
  4. Как это было проверено? Конкретные шаги, которые агент предпринял для подтверждения работоспособности реализации. Не "тесты проходят", а "я проверил критерий приёмки #3, выполнив X и наблюдая Y."
  5. Что должен проверить ревьюер? Собственная рекомендация агента о том, что заслуживает человеческого внимания. Может быть дизайн-решение, которое может пойти в любую сторону, или компромисс по производительности, который стоит второго мнения.

Ничего из этого не существует в стандартном PR-воркфлоу. Поле описания PR — это свободный текст, который никто не контролирует. PR-ы агентов имеют минимальные описания по умолчанию, потому что агенту сказали реализовать, а не документировать своё рассуждение.

Разрыв — не в инструментах. В процессе. Инфраструктура ревью существует. Не хватает структурированной записи намерения агента, которую ревьюер может сверить с diff.

Именно эту проблему решает Beadbox.

Видимость в реальном времени того, что делает весь ваш флот агентов.

Попробуйте бесплатно в период бета-тестирования →

Паттерн plan-comment-done

Вот паттерн, который закрывает разрыв без существенного оверхеда. У него три части: агент комментирует план перед написанием кода, реализует, и комментирует структурированный done-отчёт по завершении.

Шаг 1: Комментарий с планом

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

PLAN: Fix WebSocket reconnection dropping messages during
server restart.

1. Add a message buffer to hooks/use-websocket.ts that queues
   outbound messages while the connection is in CONNECTING state
2. On successful reconnection, flush the buffer in order
3. Add a 30-second timeout: if reconnection hasn't succeeded,
   surface an error to the user via the toast system
4. Update the existing reconnection test to verify buffer
   behavior

Files: hooks/use-websocket.ts, components/connection-status.tsx

Test: Unit test for buffer queueing/flushing, manual test by
killing the WS server mid-operation and verifying no messages
are lost on reconnect.

Это занимает у агента около 30 секунд. Ревьюер читает за 2 минуты. За эти 2 минуты ты ловишь проблемы, которые заняли бы 20 минут в diff-ревью:

  • Правильный ли scope? (План говорит 2 файла. Если diff затрагивает 6, что-то поехало.)
  • Подход адекватный? (Буфер сообщений с flush-on-reconnect разумен. Если бы агент предложил перезагрузить всё состояние с сервера, ты бы хотел обсудить это сначала.)
  • Есть ли пропущенные шаги? (Что с сообщениями, отправленными во время окна таймаута? Их отбрасывать или повторять?)

План — лёгкий чекпоинт. Проревьюй его, одобри, и агент продолжает с общим пониманием намерения.

Шаг 2: Реализация

Агент пишет код. Здесь ничего не меняется. Но поскольку план существует, у агента есть дорожная карта, которой можно следовать, вместо принятия ad hoc решений по ходу.

Шаг 3: Комментарий done

Когда агент заканчивает, он публикует структурированный отчёт о завершении. Не "готово." Не "реализовал фичу." Детальный отчёт о том, что реально было сделано.

DONE: WebSocket reconnection now buffers outbound messages
during CONNECTING state and flushes on successful reconnect.

Changes:
- hooks/use-websocket.ts: Added messageBuffer ref, queue
  messages when readyState !== OPEN, flush in FIFO order on
  'open' event. Added 30s reconnection timeout.
- components/connection-status.tsx: Shows "Reconnecting..."
  badge during buffer state, switches to error toast after
  timeout.

QA Verification:
1. Start dev server and WebSocket server
2. Open app, verify connection dot is green
3. Kill WebSocket server process
4. Trigger an action that sends a WS message (e.g., update
   a bead status)
5. Restart WebSocket server within 30 seconds
6. Verify: the buffered message is delivered, bead status
   updates in the UI
7. Repeat step 3, but wait >30 seconds before restart
8. Verify: error toast appears after timeout

Acceptance criteria:
- [x] Messages sent during reconnection are not lost (step 6)
- [x] Timeout surfaces user-visible error (step 8)
- [x] No behavior change when connection is stable (step 2)

Commit: f4e2a1b

Теперь у ревьюера есть всё необходимое. Он читает план (что планировалось), читает done-комментарий (что реально было построено и как проверено), и затем смотрит diff с полным контекстом. Diff-ревью превращается из "что всё это?" в "подтвержу, что это соответствует тому, что агент сказал, что сделал."

Чеклисты ревью для вывода агентов

Даже с нарративом реализации нужен систематический подход. Вот чеклист, который я использую при ревью вывода Claude Code. Он занимает 5-10 минут на ревью и ловит категории багов, которые diff-ы в одиночку пропускают.

Соответствие спецификации:

  • Реализация покрывает каждый критерий приёмки из spec?
  • Есть изменения, выходящие за рамки того, что spec запрашивал?
  • Done-комментарий сопоставляет каждый критерий с шагом верификации?

Ограничение scope:

  • Агент модифицировал только файлы, перечисленные в плане?
  • Если затронул дополнительные файлы, есть ли указанная причина?
  • Есть ли "очистительные" изменения (переименования, переформатирования, рефакторинг), которые не были частью задачи?

Покрытие тестами:

  • Существуют ли новые тесты для нового поведения?
  • Тесты действительно тестируют правильную вещь? (Агенты иногда пишут тесты, которые проходят тривиально, потому что тестируют мок, а не реализацию.)
  • Агент заявил, что запустил тесты? Есть доказательства?

Архитектурная согласованность:

  • Изменения следуют существующим паттернам в кодовой базе?
  • Есть новые абстракции, дублирующие существующие?
  • Стратегия обработки ошибок соответствует остальному проекту?

Осведомлённость о зависимостях:

  • Если агент добавил зависимости, они обоснованы?
  • Изменения ломают существующую функциональность? (Проверь файлы, импортирующие изменённые модули.)
  • Если у задачи есть зависимости от других задач, эти зависимости разрешены?

Этот чеклист работает с любым инструментом code review. Распечатай на стикере, помести в шаблон PR или встрой в CLAUDE.md, чтобы агент знал, по какому стандарту его будут оценивать. Суть не в конкретных пунктах. Суть в наличии структурированного протокола вместо "выглядит нормально."

Beads как поверхность для ревью

Паттерну plan-comment-done нужно место для жизни. Если планы и done-комментарии разбросаны по сообщениям Slack, описаниям PR и выводу терминала, ты теряешь связь между spec, планом, реализацией и верификацией.

Это проблема, которую решает beads. Beads — это open-source Git-нативный issue tracker, где каждый "bead" несёт полный жизненный цикл задачи: spec как описание, планы агента как комментарии, done-отчёты как комментарии и результаты QA как комментарии. Всё привязано к одной сущности, доступно для поиска и постоянно.

Вот как выглядит воркфлоу ревью с CLI bd:

Создать задачу со spec:

bd create --title "Fix WebSocket reconnection message loss" \
  --description "## Problem
Messages sent during WebSocket reconnection are silently
dropped...

## Acceptance Criteria
1. Messages queued during CONNECTING state are delivered
   on reconnect
2. 30-second timeout surfaces error to user
3. No behavior change when connection is stable" \
  --type bug --priority p1

Агент забирает работу и публикует план:

bd update bb-f4e2 --claim --actor eng1
bd comments add bb-f4e2 --author eng1 "PLAN: Add message
buffer to WebSocket hook...

1. Queue outbound messages when readyState !== OPEN
2. Flush buffer in FIFO order on 'open' event
3. Add 30s timeout with error toast
4. Update reconnection test

Files: hooks/use-websocket.ts, components/connection-status.tsx"

Ты ревьюишь план за 2 минуты:

bd show bb-f4e2  # Прочитать spec + комментарий с планом

Если план выглядит правильно, агент продолжает. Если нет, ты комментируешь исправления до того, как написан какой-либо код.

Агент завершает и публикует done-отчёт:

bd comments add bb-f4e2 --author eng1 "DONE: WebSocket
reconnection now buffers outbound messages...

QA Verification:
1. Kill WS server, trigger action, restart within 30s...

Acceptance criteria:
- [x] Buffered messages delivered on reconnect
- [x] Timeout error visible
- [x] No regression on stable connection

Commit: f4e2a1b"

bd update bb-f4e2 --status ready_for_qa

QA проверяет независимо:

bd show bb-f4e2  # Прочитать шаги верификации из done-комментария
# Выполнить каждый шаг
bd comments add bb-f4e2 --author qa1 "QA PASS: All 3 criteria
verified. Buffer flushes correctly, timeout fires at 30s,
stable connections unaffected."

Весь след ревью в одном месте. Через шесть месяцев, когда кто-то спросит "почему WebSocket буферизирует сообщения при переподключении?", ответ в bead: spec объясняет проблему, план объясняет подход, done-комментарий объясняет что было построено, и QA-комментарий подтверждает работоспособность.

Когда ревью в терминале достигает потолка

bd show на одной задаче даёт всё. Но когда ты ревьюишь вывод нескольких агентов через несколько параллельных рабочих потоков, CLI-воркфлоу масштабируется линейно: один bd show на задачу, один bd list чтобы увидеть готовое к ревью, один bd show на каждый план, который нужно одобрить.

Здесь вписывается Beadbox. Beadbox — это дашборд реального времени, показывающий каждую задачу в твоём workspace с текущим статусом, последним комментарием и позицией в пайплайне ревью. Ты видишь, какие агенты опубликовали планы, требующие одобрения. Какие опубликовали done-отчёты, готовые к ревью. Какие ещё в процессе. Всё обновляется в реальном времени по мере того, как агенты пишут комментарии и меняют статусы через CLI bd.

Beadbox не нужен для использования паттерна plan-comment-done. CLI справляется со всем воркфлоу. Но когда у тебя пять агентов одновременно производят ревьюируемый вывод, возможность видеть очередь ревью одним взглядом вместо опроса каждой задачи по отдельности меняет скорость прохождения через пайплайн.

Beadbox бесплатен во время бета-тестирования, а CLI beads, на котором он работает, — open-source.

Проблема ревью сама себя не решит

AI-сгенерированный код растёт быстрее, чем наша способность его ревьюить. Инструменты, которые у нас есть, были построены для другого масштаба и другого воркфлоу. GitHub PR, diff в IDE, даже продвинутый статический анализ — ни один из них не решает фундаментальную проблему: ревьюить код, не зная намерения автора, драматически сложнее, чем ревьюить код, зная его.

Решение — не лучшие инструменты diff. Это структурированное намерение: запись того, что агент планировал сделать, что реально сделал и как проверил результат. Паттерн plan-comment-done даёт эту запись без существенного оверхеда. Агент тратит 30 секунд на написание плана. Ты тратишь 2 минуты на ревью. Агент тратит 60 секунд на написание done-отчёта. Ты ревьюишь diff с полным контекстом вместо ревью с нуля.

Пять принципов на вынос:

  1. Требуй планы до кода. 30-секундный комментарий с планом экономит 20-минутные сессии ревью. Если план неправильный, исправь его до того, как код существует.

  2. Требуй структурированные done-отчёты. "Готово" — это не done-отчёт. Шаги верификации, маппинг критериев приёмки и хеши коммитов — это done-отчёт.

  3. Ревьюй по спецификации, а не по diff. Diff показывает что изменилось. Spec говорит что должно было измениться. Сопоставляй.

  4. Обеспечивай границы scope. Если агент затронул файлы вне плана, это флаг. Незапланированные изменения — это непроверенные изменения.

  5. Относись к ревью как к протоколу, а не суждению. Чеклист ловит больше багов, чем интуиция. "Выглядит нормально" — это не ревью.

Агенты будут становиться быстрее. Diff-ы будут становиться больше. Вопрос в том, масштабируется ли твой процесс ревью вместе с ними, или ты по-прежнему щуришься на 600-строчные diff-ы, надеясь на лучшее.

Если ты строишь подобные воркфлоу, поставь звезду Beadbox на GitHub.

Like what you read?

Beadbox is a real-time dashboard for AI agent coordination. Free during the beta.

Share