Чистый дифф — ещё не хорошее изменение.
Он вполне может так выглядеть. Идиоматичный, отформатированный, тесты зелёные. Хороший код по всем локальным меркам. И всё равно за ним нет замысла.
Дифф — это только код: хорошо ли читается, запускается ли, проходят ли тесты. Изменение — это решение, которое стоит за этим кодом: кто-то понял систему, увидел, зачем нужен этот шаг, и сделал его намеренно.
Раньше это понимание приходило почти случайно. Когда код писали руками, возникал побочный эффект: ты шёл искать то место, где живёт поведение, и по дороге тёрся о систему — старый тест, что сторожит всеми забытый баг; интерфейс, который можно объяснить только перемирием двух команд.
В нашем клиентском коде жил хелпер по имени parseIdsAsStrings, а при нём — дежурная шутка: по спеке эти id — числа, мы просто отказываемся её читать. Он выдирал id из сырого текста ответа, пока до них не добрался JSON.parse; на него, разумеется, был заведён тикет на снос. Потом рефакторинг протащил меня через тот угол, и, прослеживая, кто этот хелпер вызывает, я нашёл, о чём его имя тихо помнило: бэкенд раздаёт 64-битные id, JavaScript молча округляет всё, что больше 2^53, и в одну ночь — таймстемп коммита: четыре утра — соседние id начали схлопываться в одно и то же число. Функция была уродливой, потому что уродливым был инцидент, который она пережила. Тикет на снос умер в тот же день, а я ушёл, зная о системе больше, чем требовал рефакторинг.
Понимания это не гарантировало. Люди всегда катили в прод код, которого не понимали: копипаст, заклинания фреймворков, благородная традиция дёргать булев флаг туда-сюда, пока прод не перестанет орать. Но трение было — и понимание обычно вырастало как раз в нём. К диффу ты приходил уже с причинами, которые подобрал по дороге.
Теперь артефакт может появиться без этой дороги.
Агент выдаёт тебе готовый патч. Он вылизан, протестирован, к нему приложено аккуратное объяснение. Код есть. А дороги, на которой подбирались причины, — нет. Тот, у кого на руках мёрж-реквест, может сказать, что патч делает, но не почему он именно такой, а не другой, и во что обойдётся держать его живым. Полезно — и опасно по той же причине: выглядит законченным.
Сам код — это ещё полбеды. Часто он вполне нормальный; иногда — лучше, чем написал бы ты сам; а когда плохой — это знакомая проблема со знакомыми инструментами: линтеры, тесты, ревью. Настоящая проблема — правдоподобный код, за которым нет замысла. Не ошибочный. Ненамеренный. Изменение, которое никто не выбирал и за которое некому отвечать.
Это хуже бага, и намного: баг оставляет замысел целым — ты его находишь, чинишь, и починке есть с чем сверяться. А когда замысла нет, чинить нечего. Тот самый хелпер — ровно то, что ненамеренный патч сносит первым делом: он выглядит неправильным, сама спека это подтверждает, и ни один тест по нему не заплачет — в фикстурах не бывает id больше 2^53. А счёт приходит через несколько месяцев, посреди инцидента, когда отказ, который он гасил, возвращается, и никто уже не помнит, что такой отказ вообще был.
Центр тяжести ревью смещается. Раньше это была охота на дефекты: найти баг, учуять душок раньше, чем починка потребует плана миграции. Баги не уволились, когда автодополнение пошло на повышение, — но это та часть, для которой инструменты уже есть. Сложнее — выяснить, понимает ли изменение хоть кто-нибудь. Причём буквально хоть кто-нибудь: модель, которая его написала, бегло перескажет его, не понимая ни строчки. Разбирая сгенерированный мёрж-реквест, ты не столько читаешь код, сколько восстанавливаешь ход мысли, которого, возможно, никогда не было.
Я сам такие патчи мёржил. Я мог сказать, что они делают. Почему они получились именно такими, меня никто не спросил — и, помню, я был этому рад.
Поэтому контракт, которого стоит требовать, — не «вот мой дифф», а «спрашивай про него что угодно». Что сломается, если это откатить? Почему такое решение, а не очевидное? Что ты попробовал сначала и почему выбросил? Во что обойдётся держать это живым? Ответы — та часть изменения, которую Git показать не может.
Агент, конечно, охотно сгенерирует и ответы: правдоподобную проблему, правдоподобные ограничения, правдоподобно отвергнутые альтернативы — всё тем же ровным уверенным голосом. Поэтому контракт не может быть бумажкой, приколотой к мёрж-реквесту. Он ближе к присяге: проверка не в том, что ответы существуют, а в том, выдержит ли человек следующий вопрос — тот, которого не было в шаблоне, — потому что, когда изменение откажет, в комнате окажется человек. Транскрипта там не будет.
Гордость никогда не была в том, чтобы набивать код. Это был труд, и эту гонку машина выигрывает ещё до обеда. Гордость — понимать систему настолько, чтобы знать, место ли в ней этому изменению.
Это всегда и было работой. Питер Наур сформулировал это ещё в 1985-м: программа — теория, которая живёт в головах её авторов, а текст без теории мёртв. ИИ проблему не создал. Он просто сделал мёртвый текст дешёвым — и убрал трение, в котором теория раньше вырастала сама собой. Теперь за ней нужно идти намеренно.
Иногда это значит выкатить патч под своим именем — и отвечать за него.
А иногда — сказать «нет» идеально чистому диффу.






