Есть схема, которая выглядит очень удобно.
developer
↓
свой код
↓
свой review
У людей мы почти никогда не считаем это полноценным code review. Можно перечитать собственный diff. Можно найти опечатку. Можно заметить лишний запрос к базе. Можно увидеть забытый тест. Но полноценный review обычно означает, что на решение посмотрит кто-то ещё.
Другой человек. С другой историей мыслей. С другими предположениями. С другой точкой входа в задачу.
И мне довольно долго было непонятно, почему с AI мы вдруг решили, что этот принцип больше не нужен.
С AI удобно: он умеет проверять сам себя
Это одна из самых привлекательных вещей в coding agents. Дал задачу. Агент исследовал проект. Написал реализацию. Запустил тесты. Потом можно сказать:
Теперь внимательно проверь всё, что сделал.
И он действительно проверит. Посмотрит diff. Найдёт пропущенный импорт. Увидит лишнюю сложность. Добавит тест. Заметит неучтённый edge case. Иногда даже откатит часть собственного решения.
Получается почти замкнутый цикл:
задача
↓
implementation
↓
self-review
↓
fix
↓
verification
↓
готово
Очень удобно. И совершенно естественно начать считать, что этого достаточно. Я какое-то время так и делал.
Проблема не в том, что AI не умеет критиковать собственный код
Умеет. Иногда довольно хорошо. Проблема в другом.
Когда агент пишет код, он сначала строит некоторую модель задачи. Например:
Эта feature должна быть реализована через новый application service.
После этого вся работа развивается внутри этого предположения. Он создаёт service. Подключает зависимости. Добавляет тесты. Потом получает команду «проведи review» и начинает проверять уже написанное решение.
Но исходная рамка никуда не исчезла. Review легко превращается в вопрос: хорошо ли реализован новый application service? Хотя независимый reviewer мог бы начать с другого: а зачем здесь вообще новый application service?
Вот эта разница для меня стала ключевой.
Автор проверяет реализацию внутри собственного понимания задачи
Когда я сам пишу код, со мной происходит примерно то же самое. Допустим, я решил, что проблема находится в caching. Провёл час в этом направлении. Нашёл несколько подозрительных мест. Внёс изменения. В конце перечитал diff.
С большой вероятностью я буду проверять: правильно ли я исправил caching? Другой разработчик может посмотреть на задачу и спросить: а почему ты вообще решил, что проблема в caching?
И внезапно выяснится, что весь час мы исследовали не ту часть системы. Это не означает, что второй разработчик всегда умнее. Он просто не прошёл тот же путь. И поэтому у него остаётся возможность подвергнуть сомнению то, что для автора уже превратилось в основание решения.
С AI эта проблема становится особенно незаметной
Потому что модель очень хорошо объясняет собственную работу. Она может написать: я выбрал этот подход, потому что он соответствует существующей архитектуре, минимизирует изменения и сохраняет backward compatibility. Звучит убедительно.
После такого объяснения решение психологически начинает выглядеть ещё надёжнее. Потом тот же агент проводит review и говорит: существенных проблем не обнаружено.
И получается красивый замкнутый контур. Он придумал решение. Он его реализовал. Он объяснил, почему оно хорошее. Он его проверил. И он же подтвердил, что всё в порядке. Именно здесь мне становится немного некомфортно.
У людей мы специально создаём независимость
Code review вообще существует не только для поиска синтаксических ошибок. Для этого давно есть инструменты. Линтеры. Статический анализ. Тесты. CI.
Review нужен ещё и потому, что другой человек видит решение без полного набора мыслей автора. Разработчик говорит: я сделал так, потому что... Reviewer отвечает: а почему не использовать уже существующий механизм? Или: ты учёл вот этот сценарий? Или: требование вообще говорит о другом.
Вот эта независимость взгляда — важная часть review. И если она полезна для людей, мне сложно найти причину, почему она должна исчезнуть только потому, что код теперь пишет AI.
Self-review я всё равно считаю обязательным
Здесь важно не уйти в противоположную крайность. Я не хочу отказаться от self-review. Наоборот. После реализации агент должен проверить собственную работу. Это базовый уровень качества.
Например:
implementation
↓
прочитать итоговый diff
↓
проверить задачу
↓
запустить тесты
↓
запустить static analysis
↓
проверить edge cases
Очень многие проблемы можно поймать именно здесь. Забытый файл. Ошибочное имя. Несогласованный контракт. Плохой тест. Необработанное исключение. Нет никакого смысла передавать всё это независимому reviewer, если автор способен исправить очевидное самостоятельно.
Но для меня self-review отвечает на вопрос: нет ли очевидных проблем в моей реализации? А независимый review должен задавать ещё один: нет ли проблемы в самом моём понимании задачи?
Это разные уровни проверки
Я постепенно начал представлять процесс примерно так:
код
↓
self-check
↓
independent review
↓
human review
Не для каждой маленькой задачи. Но для достаточно важных изменений. Причём каждый слой полезен по-разному.
Self-check
Автор лучше всех знает, что собирался сделать. Он может быстро заметить: здесь я забыл обновить второй тест.
Independent review
Второй агент не обязан разделять исходный замысел. Он может спросить: почему вообще выбран такой подход?
Human review
Человек может увидеть то, что оба агента не знают: это технически правильно, но продукт не должен так работать.
И мне нравится именно такая многослойность. Не потому, что чем больше проверок, тем автоматически лучше. А потому, что они проверяют разные вещи.
Независимый reviewer иногда ломает именно эту связку
Второй агент может посмотреть на тест и спросить: почему ожидаемое поведение здесь считается правильным? Очень хороший вопрос. Потому что тесты обладают неприятным свойством. Как только они написаны, они начинают выглядеть как истина. Есть expectation. Он зелёный. Значит, система работает правильно.
Но тест может всего лишь формализовать первоначальную ошибку. Поэтому независимый review тестов для меня не менее важен, чем review production-кода.
Иногда я прошу reviewer начать именно с тестов
Это довольно интересный приём. Не смотреть сначала на implementation. А спросить: по тестам объясни, какое поведение добавляет этот diff. Если описание совпадает с исходной задачей — хороший сигнал. Если нет, уже есть повод разбираться.
Потом: какие важные сценарии не отражены в тестах? И только затем: теперь посмотри реализацию.
Так reviewer меньше привязывается к конкретной структуре кода. Сначала он пытается понять контракт поведения. Мне нравится этот порядок именно потому, что review чуть меньше превращается в обсуждение naming и abstractions.
Но независимость легко случайно уничтожить
Например, первый агент пишет длинное объяснение: я исследовал A, затем обнаружил B, поэтому выбрал C вместо D... После этого я копирую весь диалог второму агенту: проверь. Теперь второй reviewer уже получил готовую историю. Он знает, почему автор выбрал C. И ему проще принять эту рамку.
Иногда это полезно. Если нужно проверить конкретную реализацию. Но если я хочу действительно независимый взгляд, лучше дать меньше. Например:
исходная задача
+
обязательные ограничения
+
код проекта
+
diff
И спросить: как ты оцениваешь это решение? Без автобиографии автора.
Независимый review не обязательно требует другой модели
Это тоже важное уточнение. Мне нравится использовать разные инструменты. Например, Claude Code пишет, Codex проверяет. Но сама идея шире.
Можно использовать одну и ту же модель в независимой сессии. Если reviewer не получает предыдущую цепочку обсуждений и начинает с исходной задачи, это уже другая точка зрения.
Другая модель полезна потому, что добавляет ещё одно различие. Другие склонности. Другой способ исследовать проект. Другой стиль аргументации. Но ключевой принцип для меня не «reviewer обязательно должен называться иначе». А: reviewer не должен быть продолжением той же самой линии решения.
Ещё интереснее adversarial review
В обычном review вопрос часто звучит: найди проблемы. Но можно сформулировать жёстче: попытайся доказать, что это решение неправильное. Не в смысле придумывать вымышленные ошибки. А целенаправленно искать слабые предположения.
Например:
какое предположение здесь не доказано?
какой сценарий разрушает решение?
какое существующее поведение могло быть забыто?
что произойдёт при повторном вызове?
что произойдёт при частичной ошибке?
можно ли обойти invariant другим entry point?
Это уже почти adversarial review. И мне он нравится именно для рискованных изменений. Потому что обычная команда «проверь код» может привести к довольно дружелюбной оценке. А «попытайся найти способ, которым это решение сломается» задаёт другую роль.
Хотя здесь тоже очень легко получить шум
AI прекрасно умеет находить потенциальные проблемы, которых на практике нет. Особенно когда его специально просишь быть критичным. Начинаются сценарии: теоретически при миллионе параллельных запросов... Если в будущем появится ещё пять providers... В редкой конфигурации...
Поэтому adversarial review тоже требует ограничений. Я стараюсь просить: для каждого замечания покажи конкретное основание в текущем коде, требовании или реальном сценарии. Если основания нет, это не defect. Это гипотеза. Она может быть интересной. Но её нельзя смешивать с реальной проблемой.
Мне вообще всё меньше нравится слово «проверь»
Оно слишком широкое. Когда говоришь «проверь реализацию», непонятно, что именно ожидается. Теперь полезнее разделять:
проверь correctness
проверь соответствие требованиям
проверь архитектурные ограничения
проверь backward compatibility
проверь edge cases
проверь тесты
попытайся найти альтернативное решение
Разные виды review дают разные результаты. И это ещё одна причина не отдавать всё одному агенту одной командой. Можно распределить углы атаки.
Второй агент не должен автоматически побеждать первого
Это стало для меня важным правилом после первых cross-review. Допустим, reviewer пишет: нужно вынести этот код в отдельный Strategy. Нельзя просто отправить автору: исправь. Правильнее: оцени замечание. Есть ли конкретная проблема в текущем варианте?
Пусть первый агент ответит. Может оказаться: Strategy здесь увеличит сложность без практической пользы. Тогда замечание можно отклонить. И это нормально.
Независимый review нужен не для того, чтобы второй агент стал начальником первого. Нужен конфликт точек зрения. Это я уже видел в эксперименте, где Claude Code писал, а Codex шёл проверять.
Иногда лучший результат review — «оставить как есть»
Это вполне хороший исход. Reviewer задаёт несколько вопросов. Автор отвечает. Проверяются тесты. Исследуется существующая архитектура. И в конце решение остаётся прежним.
На первый взгляд можно сказать: зачем тогда вообще был review? Но теперь у решения есть ещё одно свойство. Оно пережило независимую попытку найти проблему. Это не гарантия правильности. Но качество уверенности немного другое.
Иногда один короткий вопрос меняет всё
Самый полезный review не обязательно содержит двадцать замечаний. Иногда хватает одного: почему здесь хранится отдельное состояние, если его можно вычислить? И внезапно становится понятно, что половина diff вообще не нужна.
Или: почему этот invariant проверяется только в HTTP-layer? И приходится переносить правило глубже. Или: этот тест проверяет реализацию. Какой тест проверяет исходное требование?
Вот ради таких вопросов я и не хочу ограничиваться self-review. Потому что автор уже мог перестать их задавать.
Это очень похоже на обычную команду
Чем больше я экспериментирую с несколькими AI, тем меньше всё это кажется какой-то совершенно новой дисциплиной. Есть автор. Есть reviewer. Есть требования. Есть тесты. Есть разногласия. Есть замечания, которые принимаются. Есть замечания, которые отклоняются. Есть момент: а давай ещё раз проверим исходное предположение.
Очень знакомая инженерная культура. Разница лишь в том, что теперь автор и reviewer могут быть моделями. Но принцип независимого взгляда от этого почему-то не становится менее полезным.
Поэтому мой вопрос теперь звучит иначе
Вопрос уже не в том, может ли AI проверить свой код. Конечно, может. Мне интереснее: достаточно ли этого?
Для маленького изменения — иногда вполне. Для механического refactoring — возможно. Для изменения, где есть бизнес-логика, несколько слоёв системы или дорогая ошибка, мне всё меньше хочется полагаться только на замкнутый цикл:
я придумал
↓
я написал
↓
я проверил
↓
я подтвердил, что всё правильно
Даже если вместо каждого «я» стоит очень сильная модель.
Самопроверка нужна. Независимость тоже
Наверное, именно так я сегодня формулирую этот принцип. Я хочу, чтобы coding agent проверял себя. Запускал тесты. Читал diff. Искал ошибки. Сверял результат с задачей.
Но я не хочу, чтобы он был единственным источником уверенности в собственной работе. Потому что самопроверка хорошо отвечает на вопрос: насколько хорошо реализован мой замысел?
А независимый review способен задать другой: почему мы вообще считаем этот замысел правильным?
У людей эта разница давно считается достаточно важной, чтобы строить вокруг неё code review. Не вижу причин отказываться от неё только потому, что теперь часть команды состоит из AI.