Проверка понимания кода ИИ или тесты: что выбрать
Тесты и линтеры не ловят главный риск кода от ИИ: вы его не понимаете. Сравниваем проверки понимания и автотесты, когда что выигрывает.

Вот сценарий, который становится всё более привычным: ваш агент для кодинга пишет 600 строк в четырёх файлах, тесты проходят, линтер доволен, и вы мержите. Через две недели всё ломается в продакшене, и вы смотрите на диф, как на чужой код, потому что, по сути, так оно и есть. Сбой был не в коде. Он был в разрыве между кодом и вашей ментальной моделью того, что он делает. Именно этот разрыв пытаются закрыть проверки понимания кода ИИ, то есть гейты, которые задают вопросы по диффу до того, как код уйдёт в релиз. На этой неделе много обсуждали Sphinx, новый open-source Git-хук, который делает именно это: перед тем как ваш push пройдёт, он читает диф и спрашивает, что код делает, когда что-то идёт не так.
Мой инстинкт бывшего ML-инженера был скептическим. Ещё один гейт? Ещё один ритуал? Но чем больше я думал о реальных сбоях, которые вижу в коде, написанном агентами, тем больше убеждаюсь, что эта идея бьёт по тому, с чем тесты структурно справиться не могут. Поэтому давайте честно сравним: проверки понимания против страховочной сетки, которая у вас уже есть, то есть тестов, линтеров, type checker'ов и CI, и разберёмся, когда выигрывает каждый из них.
Что на самом деле проверяют тесты и линтеры
Автоматические проверки отвечают на один вопрос: ведёт ли код себя так, как ожидают проверки? Это замкнутый цикл внутри машины. Тест фиксирует чьё-то представление о правильном поведении, код либо ему соответствует, либо нет, и на этапе мержа не нужно никакое человеческое понимание. Это достоинство: именно оно позволяет сотне инженеров пушить в один репозиторий, не читая работу друг друга.
Проблема в допущении, заложенном в эту модель: что человек, который мержит, точно понимает, что код должен делать, и что набор тестов это отражает. С кодом, написанным вручную, это допущение в основном выполняется: вы его писали, вы с ним мучились, вы знаете, где зарыты трупы. С кодом от агента оно незаметно ломается. Вы описали цель, агент выдал реализацию, а тесты проходят, потому что их тоже написал агент. Зелёная галочка теперь подтверждает, что код агента соответствует тестам агента. Ваше понимание в этом цикле так и не участвовало.
Это та же ловушка, о которой я писал в статье почему AI-бенчмарки не видят большую часть реальной работы: метрика может подтвердить только то, что измеряет. Набор тестов измеряет соответствие поведения на тех случаях, которые кто-то догадался закодировать. Он ничего не говорит о том, не задвоит ли retry-логика списание у клиента при таймауте Stripe, не выбрал ли агент вообще не тот эндпоинт или не «починил» ли он состояние гонки, проглотив исключение. Это ошибки понимания, и они спокойно проходят CI.
Что проверки понимания ловят, а тесты пропускают
Проверка понимания меняет направление верификации. Вместо вопроса «работает ли код?» она спрашивает: «знает ли человек, который это отправляет, что код делает, когда что-то идёт не так?» Иллюстративный пример у Sphinx хороший: ваш обработчик вебхуков теперь сохраняет ID события Stripe до вызова charge(). Что произойдёт при повторе после таймаута? Если вы скажете «клиента спишут дважды», вы ошибётесь, потому что сохранённый ID обрывает повтор. Но дальше: что будет, если сам charge() упадёт? Следующий повтор пропустит списание. Клиента никогда не спишут, и ни один тест не упадёт, потому что это выглядящий правильным паттерн идемпотентности, применённый в неправильном порядке.
Обратите внимание, что это за баг. Это не синтаксическая ошибка, не ошибка типов и не упавший assert. Это дефект проектирования, который проявляется только при определённой последовательности сбоев, и единственный дешёвый способ поймать его до продакшена: чтобы человек проанализировал путь ошибки. Тесты могли бы его поймать, если бы кто-то написал тест на «списание падает после того, как ID события сохранён». Но человек, который написал бы такой тест, это тот, кто понимал риск порядка операций, а если бы такой человек был, мы бы сюда не пришли. Проверки понимания ищут именно то понимание, которое написало бы недостающий тест.
Есть и второе преимущество, которое связано не столько с поимкой багов, сколько с обращением медленной утечки навыков. Многие разработчики описывают один и тот же опыт: гоняя агента до рабочего результата, тратишь реальные силы, но ничего не усваиваешь. Вы выпускаете больше и понимаете меньше, и разрыв растёт. Викторина в момент push заставляет хотя бы поверхностно восстановить логику диффа, и этого часто достаточно, чтобы «код, который я одобрил», снова стал «кодом, за который я отвечаю». Я уже писал, что ИИ-инструменты незаметно меняют то, как думают разработчики; такие гейты — один из первых инструментов, которые тянут в противоположную сторону.
Когда выигрывают автоматические тесты
Не будем романтизировать человеческий гейт. Тесты в нескольких областях выигрывают однозначно, и делать вид, что это не так, значит остаться с викториной как единственной страховкой.
- Обнаружение регрессий в масштабе. Ничья ментальная модель не выдержит рефакторинг на 30 000 строк. Тесты перепроверяют тысячи поведений на каждый коммит за копейки. Проверка понимания может охватить от силы три вопроса по одному диффу.
- Механическая корректность. Ошибки на единицу, неправильные форматные строки, некорректная работа с часовыми поясами: машины лучше исчерпывающе проверяют такие вещи, чем человек, отвечающий на вопросы под давлением времени.
- Повторяемость. Тест работает одинаково всегда. Качество вопроса викторины зависит от того, какая модель его сгенерировала и насколько вы бодры в пять вечера в пятницу.
- Уверенность при рефакторинге. Когда поведение вообще не должно меняться, тесты — единственная проверка, которая что-то значит. Понимание кода не помогает убедиться, что вы его не изменили.
Есть также класс кода, где проверки понимания добавляют трение без пользы: сгенерированный шаблонный код, механические миграции, обновления зависимостей, изменения конфигурации. Экзамен по тому, что делает reformat от Prettier, это налог, а не гейт. Любой инструмент в этой нише должен нормально обрабатывать низкорисковые диффы, иначе люди будут его обходить. Показательно, что Sphinx это предусмотрел: есть команда sphinx yolo как лазейка, которая логирует каждый пропуск. Логирование важнее, чем кажется: гейт, который все молча обходят, хуже, чем отсутствие гейта.

Когда выигрывают проверки понимания
Проверки понимания выигрывают именно там, где человек — самое слабое звено, а в разработке с агентами это случается чаще, чем хотелось бы признать.
- Рассуждения о пути ошибки. Повторы, таймауты, частичные записи, распределённые транзакции: пути выполнения, которые срабатывают только тогда, когда что-то уже сломалось. Агенты неплохо справляются со счастливым путём и беспечны к несчастливому.
- Диффы с большим радиусом поражения. Платежи, авторизация, миграции данных, всё, что касается систем, которые должны противостоять катастрофам. Цена неверного ответа здесь во много раз больше, чем две минуты трения от викторины.
- Код, который вы не писали и почти не ревьюили. PR на 1500 строк от коллеги не из разработки, автономная ночная ветка агента. Один практик рассказывал, что использует промпт «проверь меня на этом PR», чтобы оставаться сосредоточенным при структурированном ревью именно таких изменений.
- Онбординг и поддержание навыков. Если команда выпускает код от агентов быстрее, чем кто-либо изучает предметную область, гейт понимания — самый дешёвый принудительный механизм из доступных.
Общая мысль: проверки понимания окупаются тогда, когда диф значимый, а ментальная модель человека вызывает сомнения. Это узкая ниша, а не универсальное решение.
Слабые стороны самого гейта
Я бы не оказал вам услугу, если бы не привёл честный список того, что может пойти не так, ведь у скептиков есть реальные аргументы.
Во-первых, усталость от подтверждений. Мы через это уже проходили: баннеры с cookie, диалоги «вы уверены?», контрольные вопросы безопасности, которые все щёлкают не глядя. Гейт, который срабатывает на каждый push, приучает отвечать быстро, а не думать, и через месяц викторина превращается в театр ритуалов. Решение в том, чтобы ограничить область: запускать его на защищённых ветках и в рискованных путях, а не на каждом коммите в feature-ветке.
Во-вторых, качество вопросов. Викторину генерирует LLM, читая диф, а значит, она наследует сбои LLM: поверхностные вопросы, неверные «правильные» ответы, зацикливание на очевидном изменении при пропуске тонкого. Один ревьюер отметил, что его версия помогла структурировать ревью, но не поймала плохие решения, например вызов не того эндпоинта. Плохой вопрос хуже отсутствия вопроса, потому что он подтверждает понимание, которое не проверил. Это реальное ограничение, и я не думаю, что его можно полностью решить; по той же причине в таких инструментах дизайн агентной обвязки важнее выбора модели.
В-третьих, и это меня рассмешило, потому что так и есть: кто-нибудь обязательно направит агента на гейт, чтобы тот отвечал на вопросы вместо него. Дизайн инструмента учитывает это: ваши ответы никогда не попадают к агенту для кодинга, а секреты остаются закрытыми. Но ничто не мешает упорному разработчику вставить вопрос в окно чата. В этот момент инструмент измеряет готовность жульничать, а это культурная проблема, которую никакой хук не решит.
Гейт понимания не проверяет код. Он проверяет, что человек с контекстом и ответственностью реально в него вник, а именно это незаметно убрали агентные процессы.
Встраиваем гейт в реальный процесс
Если хотите попробовать, есть два практических варианта. Легковесный путь — навык на основе промпта в вашем агенте: «проверь меня по текущей ветке перед тем, как открыть PR». Несколько человек сделали именно так и отмечают, что это хорошо структурирует ревью, но учтите: это работает, только если вы сами решили запустить, а это как раз не срабатывает, когда вы спешите сильнее всего.
Принудительный путь — Git-хук. Sphinx ставится за минуту: npm install -g sphinx-understand, затем sphinx install, который подключает Git и ваших агентов для кодинга и спрашивает, какая модель должна проверять. Работает с Claude Code, Codex, Cursor и другими или с обычным Git, а может использовать любой OpenAI-совместимый эндпоинт. Если хотите собрать своё, основа — это хук pre-push:
#!/usr/bin/env bash
# .git/hooks/pre-push — quiz gate for protected branches
protected_branch="main"
current_branch=$(git rev-parse --abbrev-ref HEAD)
if [ "$current_branch" != "$protected_branch" ]; then
exit 0 # only gate the branches that matter
fi
diff=$(git diff origin/main...HEAD)
if [ -z "$diff" ]; then
exit 0
fi
# Send the diff to your model of choice, get a scenario question,
# check the answer. Pseudocode for the core loop:
question=$(quiz_generate "$diff")
echo "$question"
read -r answer
if ! quiz_verify "$diff" "$question" "$answer"; then
echo "Not quite — here's why. Push blocked; try again."
exit 1
fi
Два архитектурных решения важнее самого кода. Запускайте гейт на ветках, а не на пушах: срабатывайте на пуши в main и release-ветки, а в идеале ещё и на «запрос ревью», потому что поймать мусор до того, как он заберёт внимание коллеги, пожалуй, самое ценное место для гейта. И логируйте каждый обход. Лазейка необходима; журнал аудита — то, что держит систему честной.
Также учтите, где это стоит относительно ревью людьми. Я писал о том, как каждый уровень ревью замедляет команду, и по-прежнему так считаю, поэтому не добавляйте это поверх всего остального. Используйте, чтобы заменить первый, самый поверхностный проход ревью: проход «а вообще автор знает, что это делает?». Именно его проверки понимания действительно автоматизируют.
Рекомендация
Тесты и проверки понимания не конкуренты: они проверяют разные вещи. Тесты проверяют код; проверки понимания проверяют человека, ответственного за него. Разработка с агентами сломала неявное допущение, что это одно и то же, и такие гейты — прагматичная заплатка, а не трюк.
Моя рекомендация: оставьте набор тестов главной страховочной сеткой, ничто здесь его не заменит, и добавляйте гейт понимания точечно, на защищённых ветках и в рискованных путях, с логируемым лазом. Если большую часть кода вы пишете вручную, он вам не нужен; ценность гейта растёт с долей диффа, который вы не писали. И если викторина начинает казаться рутиной, которую обходят, это тоже сигнал: значит, вы отправляете код, который не понимаете, достаточно быстро, и двухминутная пауза кажется дорогой. Подумайте об этом. Неудобная версия тезиса этого инструмента верная: можно делегировать мышление, но понимание должно оставаться вашим, потому что в три часа ночи, когда цикл повторов списывает деньги без кого-либо, агент не дежурит. Дежурите вы.


