Открыл MR на ревью. 847 строк. Тесты зелёные. Линтер чистый. Покрытие 91%.

Одобрил.

Через два дня - баг на проде. Webhook от платёжки возвращал 500 на определённой комбинации параметров. Полез разбираться. Смотрю в код и понимаю: я не помню, почему тут именно такая логика. Открыл git blame. Коммит мой. Ну, формально мой - Claude написал, я замержил.

Самое неприятное - я этот код одобрил не потому, что разобрался. А потому, что он выглядел нормально. Тесты прошли. Линтер не ругался. Я решил, что этого достаточно. Не было.

211 миллионов строк

GitClear проанализировали 211 миллионов строк кода за 2020–2025. Не опрос, не self-report - чистый анализ коммитов по тысячам репозиториев. Два числа, которые стоит запомнить: code churn - строки, переписанные в течение двух недель после коммита - вырос вдвое. Рефакторинг упал на 60%.

Переводя на человеческий: код пишется и тут же переделывается. А привычка выносить повторяющееся в абстракции - исчезает. По данным GitClear, дублированный код впервые превысил вынесенный. AI хорошо копирует. Рефакторить - не просит.

Можно сказать: ну, инструменты улучшатся. Через год будет лучше. Может, и так. Но проблема не в качестве генерации.

Проблема, которую не видно в Jira

Addy Osmani - engineering lead в Google Chrome - в марте ввёл термин comprehension debt. По-русски - долг понимания. Разрыв между тем, сколько кода в системе, и тем, сколько из него хоть кто-то реально понимает.

Техдолг - штука неприятная, но хотя бы видимая. Медленные билды, запутанные зависимости, тот модуль, который все боятся трогать. Вы знаете, где он. Вы выбрали этот шорткат осознанно. Запланируете рефакторинг - в следующем квартале, ну или в следующем.

Долг понимания - невидимый. Код чистый. Тесты зелёные. Velocity растёт. DORA-метрики в норме. Sonar опросили тысячи разработчиков - 84% используют AI, при этом только 29% доверяют его коду (было 43% в 2024). Используют, но не доверяют. А потом в три часа ночи падает прод, и оказывается, что никто не может объяснить, почему эндпоинт ведёт себя именно так. Потому что никто этот код не писал. В привычном смысле этого слова.

Osmani приводит исследование: студенческая команда на седьмой неделе проекта - простые изменения ломали всё подряд. Код был нормальный. Но никто в команде не мог объяснить, почему были приняты те или иные решения. «Теория системы испарилась» - его формулировка.

И тут есть штука, которая меня зацепила больше остальных. Osmani пишет: «AI generates code far faster than humans can evaluate it.» Скорость генерации - выше скорости ревью. Раньше джуниор писал медленнее, чем сеньор ревьюил. Ревью было узким местом, но полезным - оно распространяло понимание системы по команде. AI убрал это узкое место. Вместе с пользой.

Тут, кстати, отступление. Я полгода назад скептически смотрел на такие рассуждения. Думал: это для тех, кто вайбкодит без головы. У меня ведь строго - тесты, CI, каждый MR просматриваю руками. А потом случился тот баг с вебхуком. 847 строк. Зелёный CI. Approve за 20 минут. Два дня до прода. И выяснилось, что «просмотрел руками» - это не то же самое, что «понимаю».

Голоса из окопов

На r/ExperiencedDevs кто-то задал вопрос: «Experienced teams that went hard on AI - did you agree to lower your quality bar?»

Общая картина - две команды пришли в одно и то же время, но в разные места.

Первая: менеджмент сказал ревьюерам не придираться, аутсорсеры мержат AI-код пачками, аутейджи стали длиннее и дороже. Одна из команд насчитала $10 миллионов в год на простоях - потому что при инциденте никто не мог быстро понять, что сломалось. Кто-то описал это короче: «Great engineers outputting trash. Reviews are either slow and tedious or you leak your entire codebase in source maps.»

Вторая - самый заплюсованный комментарий в треде: «Code review standard has not changed AT ALL. If it’s too big of a PR - split it up. If it’s not understood - explain it better. Speed is immaterial if you’re going the wrong way.»

Два мира. Оба используют AI. Результат - противоположный.

Тест-тавтология

Первое, что говорят в ответ на «AI-код ненадёжен»: тесты всё поймают. Вот реальный пример с моего Laravel-проекта. AI сгенерировал обработчик вебхука:

public function processWebhook(Request $request): JsonResponse
{
    $payload = $request->json()->all();
    $order = Order::where('external_id', $payload['order_id'])->first();

    if ($order && $payload['status'] === 'paid') {
        $order->update(['status' => 'completed']);
        event(new OrderCompleted($order));
    }

    return response()->json(['ok' => true]);
}

AI написал тест:

public function test_webhook_processes_paid_order(): void
{
    $order = Order::factory()->create(['external_id' => 'ext-123']);

    $this->postJson('/webhook', [
        'order_id' => 'ext-123',
        'status' => 'paid',
    ])->assertOk();

    $this->assertDatabaseHas('orders', [
        'id' => $order->id, 'status' => 'completed',
    ]);
}

Тест проходит. Coverage растёт. Все довольны.

А теперь: что будет, если order_id отсутствует в запросе? Что если вебхук пришёл дважды? Что если статус - payment_confirmed, а не paid, потому что платёжка обновила API три недели назад и новый статус пролез в прод? Что если $order не найден, а мы тихо возвращаем 200 - и платёжка считает, что всё обработано?

AI не знает, что платёжка иногда присылает дубли. Я это знаю, потому что чинил это в два часа ночи в ноябре. AI не знает - и тест не проверяет.

Osmani сформулировал точнее: «You can’t write a test for behavior you haven’t thought to specify.» Тест-тавтология - это когда AI пишет функцию, а потом AI пишет тесты для этой функции. Тесты подтверждают, что реализация соответствует реализации. Не бизнес-логике. Не граничным случаям. Не тому, что случается в три часа ночи, когда у Stripe лежит полплатформы.

Почему я не бросил AI

Звучит как аргумент удалить Claude Code и вернуться к vim. Я не собираюсь.

Anthropic опубликовали исследование: рандомизированный эксперимент, 52 профессиональных инженера, все с опытом Python, никто раньше не работал с библиотекой Trio. Половина получила доступ к GPT-4o. Половина - только документацию.

AI-группа набрала на 17% меньше на тесте понимания. 50% против 67%. Кажется, приговор.

Но если разобраться в деталях, картина сложнее.

Исследователи записали, как именно каждый участник работал с AI, и увидели шесть разных паттернов. Вот как выглядит делегатор: получает задачу, копирует условие в чат, пишет «реализуй это», получает код, вставляет, запускает. Если падает - копирует ошибку обратно в чат. Не читает код. Не спрашивает «почему». Задача - получить зелёный тест как можно быстрее. Результат на тесте понимания - ниже 40%. Быстрее всех сдал. Хуже всех понял.

А вот тот, кого исследователи назвали «conceptual inquiry»: тоже пользуется AI, но не просит писать код. Спрашивает: «как Trio отличается от asyncio», «почему тут cancel scope, а не timeout», «что произойдёт, если задача упадёт внутри nursery». Ошибки чинит сам. Результат - 65–86% понимания. И второй по скорости среди всех шести групп. Быстрее большинства делегаторов.

Тот же инструмент. Тот же AI. Разница - в вопросе. «Сделай за меня» vs «объясни мне».

52 человека - не самая жирная выборка, и тестировали конкретно обучение новой библиотеке, а не ежедневную работу со знакомым стеком. Может, я тут натягиваю сову на глобус. Но направление совпадает с тем, что я вижу у себя: когда я спрашиваю Claude «почему ты выбрал именно эту структуру», код получается лучше. А когда говорю «сделай» - получается тот самый код без автора.

Что я нащупал

Я бэкенд на Laravel, проект на ~80K строк. AI пишет примерно 60% нового кода. И где-то два месяца назад я собрал связку из фич Claude Code, которая перевернула ситуацию с comprehension debt. Не «минимизировала ущерб» - реально улучшила и код, и моё понимание.

TDD через плагин Superpowers. Есть плагин Superpowers, который заставляет Claude работать по TDD-циклу: сначала пишет один тест, запускает - убеждается что красный, потом пишет минимальную реализацию, снова запускает - зелёный. Следующий тест. По одному. Если Claude пытается написать код до теста - плагин буквально удаляет его и заставляет начать с теста. Жёстко, но работает. Тест-тавтология исчезает, потому что тест пишется до реализации и проверяет намерение, а не обратную совместимость AI-кода с самим собой. И вот что я не ожидал: наблюдая red→green в терминале, я получаю уверенность без чтения каждой строки. Тест был красным. Стал зелёным. Claude его не менял. Значит, реализация соответствует спеке.

.claude/rules/ вместо одного CLAUDE.md. Большинство знает про CLAUDE.md - один файл с правилами в корне. Но есть штука мощнее. В .claude/rules/ можно разложить правила по файлам с glob-паттернами:

.claude/rules/
  payments.md      # globs: ["**/Payment*", "**/Webhook*"]
  api.md           # globs: ["routes/api*", "*Controller*"]
  testing.md       # globs: ["*test*", "*spec*"]

Внутри payments.md - то, что Claude не может знать из кода: «Webhook от платёжки может прийти дважды - идемпотентность обязательна.» «Таймаут 30 секунд, fallback на повторную проверку.» «Ответ 200 только если заказ найден.» Эти правила подгружаются только когда Claude трогает файлы из платёжного модуля. Не засоряют контекст, когда он работает с чем-то другим.

Каждый раз, когда Claude делает что-то неочевидное - я добавляю правило. Через месяц .claude/rules/ - слепок доменного знания. AI-код перестаёт быть «чужим» - он написан по моим правилам.

Хуки - автоматика, которая не полагается на память AI. В .claude/settings.json можно повесить shell-команды на события жизненного цикла. У Claude Code их 25 - от PreToolUse (до выполнения действия) до Stop (когда агент закончил). Три хука, которые убрали у меня половину проблем:

PostToolUse на Edit|Write - автоматически прогоняет php-cs-fixer после каждого редактирования файла. Claude больше не нужно «помнить» про форматирование - хук делает это детерминированно, каждый раз.

PreToolUse на Bash - блокирует rm -rf, git push --force и DROP TABLE. Exit code 2 = Claude получает фидбэк и перестраивает подход. Работает даже в режиме автоодобрения.

Notification - macOS-уведомление, когда Claude ждёт ввода. Я перестал сидеть и пялиться в терминал. Запускаю задачу, переключаюсь на другое, получаю пуш когда пора ревьюить.

Context7 - актуальные доки вместо галлюцинаций. MCP-сервер от Upstash, open source. Подключаешь - и Claude получает документацию конкретной версии библиотеки, а не того, что было в его обучающих данных. Для Laravel это критично - API меняется между минорными версиями, а Claude уверенно пишет код по устаревшему интерфейсу. С Context7 - перестал.

Эта связка даёт compound interest. Правила в .claude/rules/ делают код предсказуемым. Хуки делают процесс детерминированным. TDD делает намерение явным. Context7 убирает галлюцинации. Вместе - AI-код перестаёт быть «кодом без автора». У него появляются мои правила, мои тесты, мои ограничения. Это не оборона от AI. Это способ выжать из AI больше, чем получает тот, кто просто нажимает «сделай».

Поворот, которого я не ожидал

На прошлой неделе я попросил Claude объяснить не свежий код, а старый. Модуль расчёта скидок, который написал вручную два года назад. Без AI. Без автокомплита. Сам, своими руками, в полном сознании.

Я не помнил, как он работает. Claude разобрал его за минуту и нашёл баг, который жил там с первого коммита. Условие, которое никогда не срабатывало из-за приоритета операторов. Два года в проде.

И тут щёлкнуло: comprehension debt существовал задолго до AI. Просто раньше мы писали мало и медленно, и не замечали. AI увеличил объём - и долг стал виден. Половина моего легаси - тоже код без автора. Автор был. Он забыл.

Это не снимает проблему. Churn вдвое - это реальная деградация. Но это меняет рамку. AI не создал comprehension debt. AI сделал его заметным. И, что иронично, AI же оказался лучшим инструментом для его погашения - потому что объяснить чужой код он умеет лучше, чем написать свой.

Недавно r/programming - 6.9 миллионов подписчиков - забанило все обсуждения LLM. 30-40% контента стало про AI, посты про алгоритмы утонули. Stack Overflow подтверждает: 84% используют, 46% не доверяют. Бунт не против технологии - против разговоров о технологии вместо разговоров о коде.

И может, правильный вопрос не «как бороться с AI-кодом», а проще: «Смогу ли я починить этот код в три часа ночи?» Если нет - не мержу. Неважно, кто написал - Claude или я сам два года назад.

P.S. Я кайфую от AI-кодинга. Три часа жизни обратно каждый день - не фигура речи. Просто у любого усилителя есть побочка. У этого - невидимая. А невидимые побочки опаснее видимых.

UPD: Пока писал, решил проверить. Вот команда, попробуйте на своём проекте:

git log --oneline --since="30 days ago" | wc -l

У меня 147 коммитов за месяц. Открыл случайные 20 - в 9 из них смог сразу объяснить, что и зачем. В остальных 11 пришлось читать diff. Это 55% «кода без автора» в моём собственном репозитории. У вас, подозреваю, картина похожая - проверьте.