Я измерил свой ИИ-кодревьюер: 93% находок и ноль ложных срабатываний

Про ИИ-ревью кода сейчас пишут много, и почти всегда одинаково: «прогнал, он нашёл баг, огонь, идём дальше». Ну просто не процесс, а сказка, а ещё точнее анекдот. «Нашёл однажды» и «находит каждый раз» отличаются примерно так же, как «доехал» и «тормоза исправны».

В разработке своих продуктов ИИ-проверки стоят на пути к мержу с конца июня и реально блокируют выкатку, за это время через них прошло около 490 коммитов. Но «стоит в процессе» и «работает» это разные утверждения, поэтому я собрал отдельный стенд и померил.

Ниже цифры, включая ту, которая мне не нравится. За две недели она стала хуже, а не лучше, при том что в самом ревьюере ничего не менялось. Это отдельный сюжет, к нему вернусь.

Почему вообще пришлось мерить

Я делаю бэкофис для небольшого бизнеса как соло-фаундер, поэтому значительная часть рутины по понятным причинам отдана агентам, а качество держат «гейты»: перед мержем изменение проходит ревью архитектуры, ревью интерфейса, ревью безопасности и QA-ревью. Гейт может сказать «CLEARED» или «NOT CLEARED», и во втором случае ветка не едет.

Дальше начинается неприятное. Гейт это языковая модель. У неё нет фиксированного поведения, она может ответить по-разному на один и тот же вход. Значит утверждение «мой ревьюер ловит баги» само по себе не проверяемо, пока не сказано: сколько раз из скольких.

Есть ещё одна причина, более болезненная. У меня дважды был гейт, который выглядел живым и не работал:

  • автотесты, подключённые к процессу выката, одиннадцать дней пропускали 89% своего же набора. В логе стояло «24 skipped, 3 passed», и это читалось как зелёный;
  • другой скрипт проверки все свои запуски завершался кодом 137. Его собственная строка «всё хорошо» и его собственная проверка «если код не ноль, падаем» не выполнялись ни разу за всё время жизни. Зелёный и красный были неразличимы.

После второго случая стало понятно: недостаточно просто довеять гейту, его эффективность нужно измерять.

Как устроен стенд

Идея простая. Берём реальные баги, которые правда были в продукте, превращаем каждый в маленький фрагмент кода и просим гейт вынести вердикт. Знаем правильный ответ заранее, поэтому можем считать попадания.

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

Все пять дефектов взяты из моей же продуктовой истории, не выдуманы:

Я измерил свой ИИ-кодревьюер: 93% находок и ноль ложных срабатываний

Ключевая деталь в подсчёте. Мало, чтобы гейт сказал «не пропускаю». Нужно, чтобы он назвал тот самый дефект из-за которого он это делает. Поэтому у каждого кейса есть список ключевых слов, и вердикт засчитывается, только если ответ попадает в суть. Иначе легко получить красивую метрику из ревьюера, который ругается на что попало.

Каждый кейс прогоняется трижды. Одного прогона недостаточно: мне важно не «может ли поймать», а «ловит ли каждый раз».

Цифры

Прогон 10 августа 2026, 8 кейсов, по 3 прогона каждый:

Я измерил свой ИИ-кодревьюер: 93% находок и ноль ложных срабатываний

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

А вот 0,933 вместо единицы это тот самый пятнадцатый прогон.

Пятнадцатый

Не поймался кейс 01, отмена чека без проверки владельца. Причём смотрите, как именно.

Во всех трёх прогонах гейт ответил «NOT CLEARED». То есть код он не пропустил ни разу. Но в одном из трёх он написал не про владельца записи, а про отсутствие аутентификации и отсутствие защиты от CSRF.

И то и другое в этом фрагменте правда есть. Ответ не бредовый, он просто про другой дефект.

По моему правилу это засчитывается как промах, и я считаю, что правильно. Если бы я считал «заблокировал» без проверки причины, метрика была бы 1,0 и означала бы ровно ничего: ревьюер, ругающийся на всё подряд, тоже даёт «заблокировал» в ста процентах случаев.

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

Ещё одна деталь, которую стоит сказать честно. Две недели назад тот же стенд на тех же кейсах дал 1,0 и 8 из 8. Сегодня 0,933 и 7 из 8. Ничего в гейте между этими прогонами не менялось. Это и есть ответ на вопрос «зачем гонять трижды»: одиночный прогон дал бы мне красивую единицу и ложную уверенность.

Первый прогон был провальным, и виноват был не гейт

Когда стенд запустился впервые, доля ложных срабатываний оказалась 0,667. Две трети чистых кейсов ревьюер завернул. Первая реакция была очевидной: гейт перестраховывается, надо ослаблять правила.

Это была бы ошибка. Оба «ложных срабатывания» оказались багами стенда, а не гейта:

  1. Генератор контекста для ревьюера терял колонку «к чему применяется» у моих внутренних правил. В итоге правило про денежные операции применялось к обычному обновлению статуса. Ревьюер работал с испорченной копией стандарта.
  2. Ревьюеру было сказано «оценивай только то, что видишь в фрагменте». Он отказывался пропускать корректный код, потому что тот вызывал функцию проверки подписи, которой не было в кадре. Настоящий гейт в этом месте просто открывает файл и смотрит.

Починил оба, доля ложных срабатываний упала до нуля.

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

Это переносится куда угодно за пределы ИИ. Новый измерительный инструмент, показавший неожиданное число, это в первую очередь утверждение о самом инструменте, а не о том, что он измеряет.

Чего эти цифры не покрывают

Тут нужна честность, иначе весь эксперимент обесценивается.

  • Восемь кейсов это мало. Это скорее порог, а не доказательство.
  • Стенд проверяет упрощённую версию гейта. Настоящий гейт читает соседние файлы и запускает отдельного придирчивого критика. То есть стенд меряет нижнюю границу.
  • Дефекты в кейсах я выбрал сам. Выборка смещена в сторону того, что я уже научился замечать.
  • Ноль ложных срабатываний на трёх чистых кейсах это ноль на трёх кейсах, а не ноль вообще.
  • Стоимость прогонов. Около 0,27 доллара за полный прогон. Дороже всего здесь не деньги, а то, что стенд надо было сначала построить: сами прогоны стоят примерно ничего, и это аргумент гонять их чаще, а не реже.

Что из этого я забрал в работу

Короткий чек-лист. Он не про ИИ, он про любой автоматический контроль качества.

  1. Ориентироваться в логах строку, которую гейт печатает, когда всё хорошо. Если её там никогда не было, гейт ни разу не досчитал до конца.
  2. Читать счётчики, а не значок. «24 skipped, 3 passed» это зелёный значок на мёртвом наборе тестов.
  3. Считать не «заблокировал», а «заблокировал по правильной причине».
  4. Держать чистые кейсы. Без них измеряется только строгость.
  5. Гонять больше одного раза. Один прогон отвечает «может ли», а нужно «делает ли каждый раз».
  6. Новый прибор с неожиданным числом проверять раньше, чем выводы. Убедиться, что починил измерение, а не ослабили порог, можно по метрике, которая не должна была двинуться.
  7. Заводить новый кейс из каждого реального инцидента. Стенд, который всегда зелёный, это ощущение с лишними шагами.
Я измерил свой ИИ-кодревьюер: 93% находок и ноль ложных срабатываний

Дальше

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

Почти все кейсы из таблицы выше набиты на живом продукте, а не выдуманы.

Интересно другое. Если у вас ИИ-ревью стоит в процессе: вы как-нибудь проверяете, что он всё ещё работает, или он живёт на доверии? Я пока не встречал ни одного описания, где кто-то мерил бы свой ревьюер повторяемо, и мне правда любопытно, есть ли такие.

1