Проблема проверки

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

Этот совет верный и бесполезный в масштабе.

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

AI, который проверяет проблемы в коде, сгенерированном AI, ещё менее надёжен. Вы просите вероятностную систему проверить вывод другой вероятностной системы. Их режимы отказа коррелируют.

Единственный паттерн, который масштабируется, — детерминированное enforcement. Правила, которые проверяются на каждом коммите, которые нельзя переопределить усталостью или излишним оптимизмом и которые валят сборку при нарушении.

Что значит детерминированность

Детерминированность означает, что проверка даёт один и тот же результат каждый раз, независимо от того, кто её запускает и когда.

  • Правило линтера детерминировано.
  • Проверка типов детерминирована.
  • Ограничение импортов через границы детерминировано.
  • Contract tests детерминированы.
  • Внимание человека, который проверяет код, — нет.
  • Оценка LLM — нет.

Это различие ещё важнее в codebases, сгенерированных AI, потому что объём сгенерированного кода превосходит то, что любая команда может проверить вручную. Вы не можете масштабировать проверку линейно со скоростью генерации. Масштабировать deterministic checks, наоборот, тривиально.

Стек guardrails

Для codebases, сгенерированных AI, стек guardrails состоит из четырёх слоёв:

Слой 1: система типов

Система типов — первый guardrail. Строгая типизация ловит класс ошибок, который ни один процесс проверки — человеческий или AI — не ловит стабильно:

  • Нарушения null-ограничений
  • Несовпадения interface
  • Необработанные варианты
  • Неправильные типы аргументов

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

Слой 2: правила архитектуры

Правила архитектуры жёстко удерживают дисциплину границ:

  • Ни один экран не импортирует внутренности другого экрана.
  • Доменная логика не импортирует инфраструктуру напрямую.
  • Ни один module не обходит composition root ради доступа к зависимостям.
  • Ни один SDK вендора не появляется вне своего module-адаптера.

Инструменты вроде Semgrep, ArchUnit, dependency-cruiser или кастомных правил ESLint могут статически проверять это. Ключевой момент в том, что они работают в CI и валят сборку. Предупреждение, которое разработчик может проигнорировать, — не guardrail.

Слой 3: Contract Tests

Contract tests проверяют, что modules удовлетворяют своим interfaces, не запуская всю систему:

  • Адаптер аутентификации удовлетворяет interface аутентификации.
  • Адаптер аналитики удовлетворяет interface аналитики.
  • Адаптер хранилища удовлетворяет interface хранения.

Они работают быстро, проверяют границы интеграции и ловят тот специфический режим отказа, при котором код, сгенерированный AI, удовлетворяет сигнатуре типа, но нарушает поведенческий contract.

Слой 4: детерминированные интеграционные проверки

На уровне интеграции проверки подтверждают свойства всей системы:

  • composition root разрешает все зависимости без runtime-ошибок.
  • Граф зависимостей не содержит циклов.
  • Все обязательные переменные окружения объявлены.
  • Конфигурация валидна на этапе сборки, а не только на runtime.

Semgrep для кода, сгенерированного AI

Semgrep заслуживает отдельного упоминания, потому что он отлично выражает архитектурные правила в виде кода:

  • Сопоставление с AST-структурой, а не со строками.
  • Кастомные правила под конкретный проект, а не только общий линтинг.
  • Достаточно быстрый запуск на каждом коммите.
  • Достаточная выразительность, чтобы кодировать нарушения границ.

Команда, активно использующая AI-генерацию, должна поддерживать набор правил Semgrep, который кодирует её архитектурные границы. Когда AI генерирует код, нарушающий границу, сборка падает ещё до слияния. Внимание человека не требуется.

Это не про поиск багов в выводе AI. Это про то, чтобы сделать структурные нарушения невозможными для слияния независимо от того, как они были произведены.

Что на самом деле ловит AI-проверка кода

Инструменты AI-проверки кода полезны для:

  • Согласованности стиля
  • Пробелов в документации
  • Очевидных логических ошибок
  • Предложения альтернативных подходов

Инструменты AI-проверки кода ненадёжны для:

  • Нарушений архитектурных границ
  • Тонкой связанности, появляющейся между modules
  • Нарушений поведенческого contract
  • Свойств безопасности, которые требуют рассуждения о всей системе

Режим отказа не в том, что AI-проверка иногда что-то пропускает. Режим отказа в том, что она пропускает что-то непредсказуемо, и вы не можете знать, когда именно она что-то упустила. Именно поэтому она не может заменить детерминированное enforcement, а может только дополнять его.

Экономика

Детерминированные guardrails дёшевы в запуске и дороги в первоначальной настройке.

Написание архитектурных правил занимает несколько дней. Поддержка конфигурации Semgrep требует постоянного внимания. Настройка contract tests требует сначала определить interfaces.

Но как только они появляются, они запускаются на каждом коммите почти с нулевой предельной стоимостью. Они не устают. Они не пропускают проверки в пятницу после обеда. Они не уступают старшинству или социальному давлению.

Для codebases, сгенерированных AI, где объём кода высок, а скорость генерации велика, этот экономический профиль решающий. Альтернатива — масштабировать человеческую проверку до скорости AI-генерации — нежизнеспособна.

Связь с AI-native архитектурой

Об этом более широком паттерне я писал в статье Stanford CS146S прав насчет AI coding. Недостающий предмет — архитектура. Детерминированные guardrails — это механизм enforcement, который делает заменяемую архитектуру реальной.

Без guardrails архитектурные границы остаются желаемыми. С ними границы становятся структурными. Разница между «мы стараемся держать modules изолированными» и «сборка падает, если изоляция module нарушена» — это разница между архитектурой, которая переживает итерации на скорости AI, и архитектурой, которая под этой скоростью рушится.

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

Частые вопросы

Какой инструмент лучше всего подходит для enforcement архитектурных правил над кодом, сгенерированным AI?

Semgrep — самый гибкий вариант для кастомных архитектурных правил. Он поддерживает сопоставление с AST-структурой, быстро работает в CI и позволяет командам кодировать границы, специфичные для проекта. Для проектов на JavaScript/TypeScript также эффективны dependency-cruiser и кастомные правила ESLint.

Может ли AI-проверка кода заменить проверку кода человеком?

Нет. AI-проверка кода дополняет человеческую проверку в вопросах стиля и документации, но ненадёжна для enforcement архитектурных границ и свойств безопасности. Детерминированные проверки — система типов, линтеры, архитектурные правила, contract tests — остаются единственной масштабируемой заменой проверки, зависящей от внимания.

Как настроить guardrails так, чтобы не замедлить команду?

Начните с системы типов: строгий режим, без исключений. Добавьте архитектурные правила для трёх границ с наивысшим риском. Добавьте contract tests для внешних интеграций. Настройка каждого слоя занимает день, а выполняются они за секунды. Замедление возникает из-за нарушений, а не из-за самих проверок.

В чём разница между guardrail и линтером?

Линтер подсказывает улучшения. Guardrail валит сборку. Разница — в enforcement. В codebases, сгенерированных AI, подсказки в масштабе игнорируются, потому что объём слишком велик для последовательного ручного внимания. Только падение сборки гарантирует соблюдение правил.