Неверная метрика

Большинство разговоров о качестве кода, сгенерированного AI, сосредоточены на корректности в момент генерации. Компилируется ли результат? Проходит ли он тесты? Соответствует ли он спецификации?

Это базовый минимум. Он ничего не говорит о реальной стоимости.

Настоящая метрика — заменяемость: насколько дёшево вы можете удалить этот module и реализовать его заново за тем же contract, когда требования изменятся?

Если ответ — «тривиально», скорость AI начинает накапливаться. Если ответ — «сначала нужно отследить шесть неявных зависимостей», вы уже потеряли преимущество в скорости, которое думали получить.

Почему AI-код по умолчанию тяготеет к жёсткой связанности

Большие языковые модели оптимизируются под текущий запрос. Они не оптимизируются под будущие изменения.

Когда вы просите сгенерировать сценарий входа, вы получаете сценарий входа, который работает. Вы не получаете сценарий входа, спрятанный за interface аутентификации, которую можно заменить с Firebase на Supabase, а потом на кастомный сервис JWT, не трогая ни один экран, который от неё зависит.

Это не сбой модели. Это сбой контекста. Модель не просили оптимизировать под заменяемость, поэтому она этого и не сделала.

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

Заменяемость — архитектурное решение

Заменяемость не возникает случайно. Это осознанный структурный выбор:

  • Каждая внешняя зависимость находится за interface, которым владеет приложение.
  • Каждый сгенерированный module публикует contract, а не реализацию.
  • Каждая необязательная возможность внедряется, а не импортируется напрямую.
  • Каждое решение о композиции живёт в одном composition root, а не размазано по пятидесяти файлам.

Здесь нет ничего нового. Это базовая инверсия зависимостей. Новым является то, что код, сгенерированный AI, делает нарушение этих принципов лёгким и незаметным — до того момента, пока вам не понадобится что-то изменить.

Накопительный эффект

Когда каждый module заменяем:

  • Неудачные генерации стоят минут, а не дней.
  • Смена вендора сводится к замене реализации за interface, а не к rewrite.
  • Скорость AI остаётся линейной на последовательности итераций, а не становится логарифмической.
  • Команда может сказать: «сгенерируйте это заново за тем же contract» — и действительно иметь это в виду.

Когда modules перепутаны между собой:

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

Практическая проверка

Прежде чем принимать какой-либо module, сгенерированный AI, в codebase, примените одну проверку:

Могу ли я удалить этот файл и реализовать его заново с нуля, используя только тот interface, который он публикует, не изменяя ни один использующий его module?

Если да — выпускайте. Если нет — исправьте границу до выпуска.

Это дёшево внедрять. composition root плюс архитектура с приоритетом interface дают вам это свойство структурно. Вам не нужна сложная система управления. Вам нужно одно архитектурное правило, которое применяется последовательно.

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

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

Stanford учит разработчиков эффективно пользоваться AI-инструментами. Это важно. Но свободное владение инструментами без заменяемости — ловушка: вы выпускаете быстрее до тех пор, пока codebase не становится дорогой в изменении, а потом выпускаете медленнее, чем команды, которые вообще не использовали AI.

Дисциплина не в том, чтобы «лучше формулировать запросы». Дисциплина в том, чтобы «строить архитектуру так, чтобы ошибки в запросах было дёшево исправлять».

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

Что означает «заменяемая архитектура» для кода, сгенерированного AI?

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

Как обеспечить заменяемость в codebase, сгенерированной AI?

Три механизма: interfaces, которыми владеет приложение, а не зависимость; единый composition root, где связываются все реализации; и проверка в CI, которая падает, если какой-либо module напрямую импортирует внутренности другого module.

Замедляет ли заменяемость начальную разработку?

Нет. Описание interface перед генерацией реализации занимает секунды. Цена отказа от этого решения проявляется через недели, когда смена вендора или refactor превращаются в полный rewrite.

Это то же самое, что внедрение зависимостей?

Внедрение зависимостей — один из механизмов достижения заменяемости, но не вся картина целиком. Для заменяемости также нужны contract tests, проверка границ и composition root, а не только параметры конструктора.