Аудит AI-доработок кода по истории Git
Проведите аудит AI-доработок по истории Git и измерьте переписанные diff, раунды ревью, отмены и повторно открытые дефекты вместо активности инструментов.

Содержание
Статистика использования инструментов почти всегда выставляет внедрение AI-разработки в выгодном свете. Число промптов растёт, число принятых подсказок тоже, а еженедельный отчёт говорит, что команда стала работать быстрее. Тем временем pull request становятся больше, ревьюеры по два раза просят исправить одно и то же, а дефекты возвращаются после того, как кто-то объявил их исправленными.
Если вы хотите понять, улучшает ли ассистент инженерный результат, проверяйте работу, которая выдержала проверку ревью, релизом и продакшеном. История Git показывает, что команда переписывала. События pull request показывают, сколько раз ревьюеры возвращали работу на доработку. История задач показывает, действительно ли дефект оставался закрытым. Такими показателями сложнее манипулировать, поэтому их и стоит использовать.
Это не аргумент против AI-ассистентов. Это аргумент против того, чтобы считать телеметрию активности доказательством ценности. Ассистент, который создаёт полезный первый вариант, может серьёзно сэкономить время. Ассистент, который предлагает правдоподобные, но плохо ограниченные изменения, переносит усилия с набора текста на ревью, тестирование и исправления. Второй случай часто выглядит продуктивным, пока вы не изучите оставленный им след.
Активность инструмента показывает использование, а не качество инженерной работы
Доля принятых подсказок говорит, что инженер вставил предложение в редактор. Она не говорит, прошло ли предложение ревью без изменений, соответствует ли оно нужному поведению и оставалось ли в кодовой базе достаточно долго, чтобы иметь значение.
Та же проблема возникает с числом промптов, объёмом сгенерированных строк и завершёнными задачами агента. Все эти показатели говорят, что инструмент участвовал в работе. Ни один не показывает, уменьшил ли он общий объём усилий, необходимый для поставки корректного изменения.
Я видел, как команды радовались резкому росту сгенерированного кода, а опытные ревьюеры тем временем молча превращались в бригаду уборки. Ревью не провалилось. Провалилась метрика. Она наградила первый акт написания и проигнорировала часы, потраченные на удаление ошибочных предположений, восстановление пропущенных крайних случаев и объяснение, почему на первый взгляд разумное изменение не должно попадать в этот сервис.
Полезный аудит начинается с более строгой единицы анализа: запроса на изменение. Pull request, issue или связанный тикет поставки задаёт границу для намерения, обсуждения, правок, тестов и результата выпуска. Затем аудит задаёт четыре вопроса.
- Какую долю предложенного diff команда существенно переписала до merge?
- Сколько раундов ревью потребовалось до одобрения изменения?
- Пришлось ли команде позже отменить поставленное поведение или заменить его под давлением обстоятельств?
- Открылся ли снова связанный дефект после того, как кто-то его закрыл?
Эти показатели всё равно требуют суждения. И это хорошо. Инженерный результат включает решения, а дашборд, который полностью их исключает, будет поощрять неправильное поведение.
Не используйте результаты для ранжирования отдельных инженеров. При работе с AI границы авторства быстро размываются. Один человек может подготовить изменение, другой направлять ассистента, а третий убрать опасные части на ревью. Правильная единица анализа, это процесс, область репозитория, класс задач или команда. Вы проверяете, создаёт ли рабочий метод дорогие доработки.
Определите доработку до сбора первого события
Доработка, это последующее изменение, которое удаляет, заменяет или исправляет уже выполненную работу для того же результата. Это не каждая правка после первого коммита.
Такое различие не даёт превратить обычную инженерную работу в сигнал провала. Pull request часто становится лучше благодаря ревью. Если ревьюер просит добавить пропущенный тест, а автор его добавляет, это работа по завершению. Если ревьюер обнаруживает, что центральная реализация выбрала неправильную границу, и автор удаляет большую часть решения, чтобы построить другой подход, это доработка.
Используйте четыре отдельных показателя. Не объединяйте их в один общий балл с самого начала.
- Доля переписанного diff, это часть значимых строк, добавленных в рамках запроса на изменение и позже исчезнувших или существенно изменённых до merge.
- Доля отменённых изменений, это часть слитых изменений, которые позже отменили, частично откатили или заменили, потому что исходное поведение оказалось неправильным или небезопасным.
- Число раундов ревью, это количество циклов «запрос изменений, ответ» после первой версии, доступной для ревью, а не число комментариев.
- Доля повторно открытых дефектов, это доля устранённых дефектов, которые в течение выбранного периода снова перешли в активное состояние из-за того же исходного поведения.
Главная граница проходит между правкой, которая завершает исходный план, и правкой, которая заменяет план. Команды смешивают их, потому что оба случая проявляются как добавленные и удалённые строки. Но с точки зрения процесса это разные вещи.
Представим, что ассистент подготовил endpoint для биллинга. На ревью попросили тест идемпотентности, тайм-аут и более понятные имена. Эти добавления могут увеличить итоговый diff, но реализация всё ещё следует исходному замыслу. Считайте это усилием на ревью, если потребовался ещё один раунд. Не называйте исходный основной diff переписанным.
Теперь представим, что ревью обнаружило: endpoint доверяет цене, переданной клиентом, и автор удаляет сгенерированный путь расчёта цены в пользу получения котировки на сервере. Такая замена меняет модель безопасности. Считайте удалённую и заменённую реализацию переписанным diff. Это показывает, что первое решение создало убедительный, но недопустимый путь.
Запишите определения в заметке аудита на уровне репозитория до начала извлечения данных. Если два человека не могут одинаково классифицировать пять тестовых pull request, уточните правила и примеры до обработки сотен запросов. Метрика с размытыми границами создаёт масштабную ложную уверенность.
Git даёт доказательства, но не показывает всей истории
Git может установить, что патчи менялись. Но он не может надёжно сказать, почему они менялись, заблокировал ли их ревьюер и увидел ли клиент дефект позже. Эти данные дают pull request и задачи.
Для каждого запроса на изменение сохраняйте такие записи:
- базовый коммит первой версии, доступной для ревью
- head-коммит каждой версии, отправленной до merge
- merge-коммит или финальный снимок целевой ветки
- события ревью, включая запрос изменений, одобрение и разрешение комментариев
- связанные задачи, ссылки на инциденты, отмены и последующие исправляющие pull request
Первая версия, доступная для ревью, важна. Если сравнивать только первый коммит с merge-коммитом, вы смешаете обычную локальную подготовку с доработками по итогам ревью. Если сравнивать только последнюю версию с результатом merge, вы не увидите дорогие части, от которых ревьюеры заставили автора отказаться.
Создайте неизменяемую таблицу извлечения. Не позволяйте аналитику вручную переносить идентификаторы коммитов в таблицу задним числом. В базовой записи должно быть достаточно полей, чтобы воспроизвести каждое число:
{
"change_id": "PR-418",
"repository": "payments-api",
"base_sha": "a1b2c3d4",
"first_review_sha": "b2c3d4e5",
"revision_shas": ["b2c3d4e5", "c3d4e5f6", "d4e5f6a7"],
"merge_sha": "e5f6a7b8",
"review_rounds": 2,
"linked_issue_ids": ["BUG-902"],
"assistant_exposure": "declared",
"excluded_paths": ["vendor/", "generated/", "*.lock"]
}
Поле assistant_exposure должно быть необязательным и укрупнённым. Используйте его, если у команды есть надёжное объявление или соглашение на уровне репозитория, например метка AI-assisted. Не выводите его из сообщения коммита со словом «generated» или из размера diff. Люди используют инструменты по-разному, а большое изменение может быть полностью написано вручную.
Документация Git проводит здесь полезную границу. git diff сравнивает конечные точки, а git range-diff сравнивает два диапазона коммитов и пытается сопоставить соответствующие патчи в разных версиях. Поэтому сравнение диапазонов полезно исследователю, который изучает изменённую последовательность, но его не следует считать стабильным машинным интерфейсом. Git прямо описывает вывод range-diff как человекочитаемый porcelain-формат, текст которого может меняться между версиями.
Храните исходные ответы API и ссылки на объекты Git рядом с производной таблицей. Через полгода кто-то спросит, почему pull request получил три раунда вместо двух. Вы должны показать события, версии и правило классификации, а не защищать непонятную ячейку дашборда.
Считайте переписанные diff по происхождению патчей, а не по числу коммитов
Показатель переписанного diff должен отвечать на практический вопрос: какую долю значимого кода, предложенного в доступной для ревью версии, команда позже отбросила или существенно изменила до merge?
Число коммитов не подходит, потому что инженеры исправляют коммиты, объединяют работу, делают rebase или принудительно отправляют более чистую историю. В ветке может быть десять коммитов и почти не быть доработок, или два коммита и полная замена первоначального дизайна.
Начните со снимков версий. Для каждой пары последовательных версий, доступных для ревью, по возможности считайте diff от одной и той же базы. Затем определите удалённые строки, которые появились в предыдущей версии, и добавления, заменяющие то же локальное поведение в том же файле или соседнем фрагменте кода. Идеальный семантический движок сравнения кода не нужен. Нужны консервативные правила и очередь ручной проверки неоднозначных случаев.
Используйте такой расчёт:
rewritten_diff_share =
(removed_introduced_lines + materially_replaced_lines)
/ meaningful_lines_in_first_reviewable_diff
Исключайте пустые строки, чистое форматирование, сгенерированные файлы, lock-файлы, vendor-код и механические миграции во всём репозитории. Если форматирование попадёт в числитель, команда узнает только то, что форматтер часто запускается.
Следующие команды оболочки создают пакет материалов для ревью одной ветки. Сами по себе они не дают итоговую метрику. Они предоставляют аудитору фактические данные для классификации изменения.
BASE=$(git merge-base origin/main feature/quote-validation)
FIRST_REVIEW=b2c3d4e5
LATEST=d4e5f6a7
# What the first review saw
git diff --numstat "$BASE" "$FIRST_REVIEW"
# What changed between the first review and the latest revision
git diff --find-renames --word-diff=porcelain "$FIRST_REVIEW" "$LATEST"
# How the patch series changed after revision
git range-diff "$BASE".."$FIRST_REVIEW" "$BASE".."$LATEST"
Первая команда выдаёт разделённые табуляцией количество добавленных строк, количество удалённых строк и пути. Вторая создаёт построчный word diff, который можно разобрать скриптом, хотя Git предупреждает, что поведение word-diff может меняться из-за деталей реализации. Третья нужна для ручной проверки, особенно после rebase.
Для крупных аудитов создайте карту происхождения. Храните нормализованную сигнатуру патча каждой версии ревью и отслеживайте, имеет ли последующий патч ту же сигнатуру, связанный путь и hunk либо не имеет достоверной связи. patch-id в Git полезен для первого случая: он вычисляет идентификатор патча, игнорируя номера строк, а стабильный режим также игнорирует пробелы и порядок diff-файлов. Git описывает его как способ найти вероятные дублирующиеся коммиты, и именно такой уровень уверенности ему следует приписывать. Он показывает вероятное соответствие, а не одинаковое намерение.
Не называйте функцию переписанной только потому, что она перешла в другой файл. Не называйте переименование переменной заменой дизайна. Задайте пороги, требующие значимого удаления и замены в одной поведенческой области, а затем выборочно проверьте изменения с высокими значениями. Метрика, пропускающая несколько пограничных правок, лучше метрики, которая объявляет обычную уборку провалом AI.
Revert нужен код причины, иначе он будет вводить вас в заблуждение
Revert даёт более сильное доказательство, чем изменение до merge, потому что команда уже приняла работу в целевую ветку. Но и его нужно классифицировать.
Релиз-менеджер может отменить безопасную функцию, потому что клиент попросил отложить её. Команда может отменить миграцию, потому что закрылось окно развертывания. Ни один из этих случаев не говорит о плохой реализации. И наоборот, частичный откат внутри hotfix может не содержать сообщения Git Revert и оказаться более серьёзным сигналом.
Отслеживайте три категории:
- Отмена из-за корректности: код выдавал неправильное поведение, нарушал контракт или создавал проблему безопасности.
- Операционная отмена: изменение вызвало неприемлемую проблему с производительностью, надёжностью, развёртыванием или наблюдаемостью.
- Бизнес- или релизная отмена: команда убрала корректное изменение из-за смены приоритетов, сроков или решения по выпуску.
Только первые две категории входят в показатель качества. Бизнес-отмены нужно видеть, но не включать в числитель. Иначе нестабильный план развития продукта будет создавать впечатление, что инженерный процесс хуже, чем есть на самом деле.
Сначала ищите явные revert. Затем ищите исправляющие pull request, которые ссылаются на исходное изменение или затрагивают те же файлы и задачу за короткий период. Вторую группу должен классифицировать человек. Автоматизация может предложить кандидатов по связанным идентификаторам задач, сообщениям коммитов, изменённым путям и сходству патчей. Но она не может безопасно решить, что каждое близкое по времени исправление относится к исходному изменению.
Reflog Git иногда полезен при восстановлении локального расследования, поскольку хранит предыдущие положения локальных ссылок веток и HEAD. Это не надёжный источник для организационного аудита: данные локальны, могут истечь и часто отсутствуют в экспортированных данных размещённого репозитория. Используйте версии pull request на сервере и защищённую историю веток как фактическую запись.
Расчёт прост:
correctness_and_operational_revert_share =
merged_changes_with_a_qualifying_revert
/ merged_changes_in_the_cohort
Не ждите, пока накопится достаточно отмен, чтобы показатель стал статистически впечатляющим. Даже один откат из-за корректности в чувствительной области заслуживает проверки на уровне патча. Цель показателя не в создании рейтинга. Он нужен, чтобы находить типы работы, где быстрые черновики прошли мимо предусмотренных контролей.
Раунды ревью показывают, где первый вариант не попал в цель
Число комментариев плохо отражает усилия на ревью. Один внимательный ревьюер может оставить двадцать полезных комментариев за один проход. Другой трижды написать «исправьте», не объяснив проблему. Считайте циклы принятия решения.
Раунд ревью начинается, когда ревьюер запрашивает изменения, отмечает блокирующую ветку обсуждения или фиксирует явно нерешённую проблему в версии, доступной для ревью. Он заканчивается, когда автор отправляет следующую версию, отвечающую на этот набор замечаний. Одобрение без запроса изменений не создаёт нового раунда.
Так получается показатель, понятный основателям. Если раньше команда сливалa обычные изменения после одного прохода ревью, а теперь для того же типа работы нужны три прохода, ассистент может ускорять написание кода, одновременно перенося проектные решения в очередь ревью.
Нужна небольшая политика для спорных случаев:
- Несколько ревьюеров, запросивших изменения в одной версии, считаются одним раундом.
- Новое блокирующее замечание после отправки ответа считается ещё одним раундом.
- Игнорируйте неблокирующие мелкие замечания, которые не задерживают одобрение, если только ваш процесс не требует блокировать ими merge.
- Разделяйте изменение, если pull request разросся до двух несвязанных функций. Иначе число раундов ревью теряет смысл.
- Фиксируйте, обнаружил ли ревьюер пробел в спецификации, ошибку дизайна, пробел в тестах или простую ошибку реализации.
Последнее поле меняет разговор. Если дополнительные раунды в основном вызваны отсутствием продуктовых решений, обвинять ассистента для программирования было бы слишком просто. Если чаще встречаются выдуманные API, дыры в авторизации, сломанные пути обработки ошибок или тесты, проверяющие только успешный сценарий, команде нужны более точные формулировки задач и лучшие ограничения для сгенерированной работы.
Ревьюерам не нужно писать роман, чтобы данные оставались полезными. Добавьте небольшой набор меток в процесс pull request. Метку «AI-черновику понадобилась корректировка дизайна» можно сделать доступной, но не обязательной. Принудительное раскрытие заставляет людей скрывать использование инструментов. Аудит может измерять качество поставки без превращения метки в признание.
Повторно открытый дефект измеряет ложное закрытие, а не просто наличие ошибок
Повторное открытие дефекта показывает, что команда объявила поведение исправленным, а затем выяснила, что оно не было исправлено, работало только в одном сценарии или сломалось в условиях, которые первоначальная проверка не охватила.
Считайте дефект повторно открытым только тогда, когда та же задача возвращается из закрытого состояния в активное или новая задача прямо связывает себя с предыдущим дефектом как с повторным проявлением. Не считайте повторным открытием каждый новый тикет рядом с тем же компонентом. В платёжном сервисе за одну неделю может появиться несколько разных ошибок.
Определите период наблюдения с учётом частоты релизов. Для продукта с непрерывными поставками проверяйте дефекты, открытые снова в течение заданного числа дней после устранения. Для корпоративного ПО с плановыми релизами используйте период, охватывающий следующий значимый клиентский выпуск и цикл поддержки. Применяйте одинаковое правило к каждой сравниваемой когорте.
В записи аудита должны быть такие поля:
issue_id: BUG-902
original_fix_change: PR-418
resolved_at: 2026-05-07T16:40:00Z
reopened_at: 2026-05-12T09:15:00Z
reopen_reason: missing idempotency behavior on retry
linked_corrective_change: PR-431
classification: same root behavior
Не сводите это слишком рано к одному числу. Изучайте повторно открытые дефекты. Они часто показывают повторяющийся сбой, который скрывают агрегированные метрики кода: ассистент сгенерировал endpoint без модели конкурентного доступа, скопировал проверку из соседнего маршрута с другой границей доверия или написал тесты на моках, которые повторили то же ошибочное предположение.
Повторно открытый дефект также разделяет два сбоя, которые команды часто смешивают. Пропущенный дефект это ошибка, которая вышла наружу до того, как кто-то заявил, что исправил её. Ложное закрытие это ошибка, прошедшая цикл исправления и вернувшаяся снова. Второй случай говорит о критериях готовности, дизайне тестов, глубине ревью или последующих действиях после инцидента. Если у AI-assisted изменений высокая доля ложных закрытий, добавление ещё одного правила линтера редко решит исходную проблему.
Проводите аудит как сравнение когорт, а не как вердикт по одному pull request
Один неудачный pull request может быть следствием плохого дня, срочного дедлайна или сложной задачи. Аудит становится полезным, когда сравнивает похожую работу за период и показывает, где проблемы концентрируются.
Создавайте когорты, отвечающие на операционный вопрос. Сравнивайте изменения API с изменениями API, а не с правками документации. Сопоставляйте работу в процессе с AI и сравнимую работу той же команды в той же области репозитория. Если надёжных данных об использовании нет, сравните периоды до и после изменения процесса, но зафиксируйте, что ещё изменилось за это время.
Для каждой когорты отдельно показывайте такие значения:
| Показатель | Что он показывает | Чего он не показывает |
|---|---|---|
| Доля переписанного diff | Какую часть ранней реализации команда отбросила или заменила | Можно ли было избежать всех правок |
| Раунды ревью на слитое изменение | Как часто ревью требовало ещё одного цикла ответа | Насколько продуманными были комментарии |
| Доля отмен, связанных с качеством | Как часто слитую работу пришлось откатить по причине качества | Была ли отмена вызвана продуктовым приоритетом |
| Доля повторно открытых дефектов | Как часто объявленное исправление не оставалось исправлением | Общую частоту дефектов в продакшене |
Затем изучите пересечения. Высокая доля переписанного diff при обычном числе раундов ревью может означать, что авторы исправляют плохой сгенерированный код ещё до того, как его увидят ревьюеры. Это отнимает время, но требует другого решения, чем узкое место на ревью. Низкая доля переписанного diff при высокой доле повторно открытых дефектов указывает на слабое покрытие тестами, поверхностные критерии приёмки или различия между staging и production.
Самый тревожный паттерн, это высокие значения всех четырёх показателей для одного класса задач. Обычно это означает, что первые варианты выглядят достаточно аккуратно, чтобы пройти быстрый взгляд, но не содержат достаточно глубокого понимания ограничений, чтобы выдержать полный цикл поставки.
Не скрывайте знаменатель. Процент на основе трёх изменений, это повод для дальнейшего изучения, а не вывод о производительности. Показывайте рядом с каждой долей абсолютные значения, список исключений и перечень pull request, взятых для классификации. Это не позволит основателю принять кадровое решение по графику, в который незаметно попало форматирование всего репозитория.
Аудит должен привести к изменению рабочего правила
Завершённый аудит бесполезен, если заканчивается фразой «используйте AI осторожнее». Вывод должен изменить то, как работа попадает в инженерный процесс.
Если сгенерированные изменения показывают высокую долю переписанного diff в коде авторизации или биллинга, требуйте письменный инвариант и одобренный ревьюером подход до генерации. Если число раундов растёт из-за выдуманных ассистентами локальных API, добавьте в контекст задачи архитектурные заметки репозитория и контракты интерфейсов. Если дефекты открываются снова, потому что тесты покрывают только успешный сценарий, требуйте план тестирования отказов до этапа реализации.
Измените одно правило для определённой когорты, затем снова измерьте те же четыре результата. Не меняйте в один понедельник модели, промпты, правила код-ревью и структуру команды. Активности будет много, а причинного ответа не появится.
Прямой ответ таков: у многих команд нет проблемы с продуктивностью AI. У них проблема с доказательствами. Они поставили счётчик, который косвенно фиксирует нажатия клавиш, а затем приняли его за показатель поставки. История Git, события ревью и повторное появление дефектов дают более сложную, но честную картину того, куда ушла работа.
Если команда не может чисто собрать такие данные, это тоже диагностический сигнал. Аудит Team & AI должен оставить вам воспроизводимый метод извлечения, ясные исключения и короткий список изменений процесса, связанных с реальными доработками, а не с энтузиазмом вокруг инструментов.
Часто задаваемые вопросы
Можно ли измерять доработки AI по числу коммитов?
Нет. При force-push, squash merge или rebase ветки идентификаторы коммитов могут измениться, хотя код в итоге останется тем же. Сравнивайте содержимое патчей и снимки ветки вокруг событий ревью, а число коммитов используйте только как дополнительный контекст.
Что считать переписанным кодом в pull request?
Считайте код переписанным, если последующее изменение удаляет или существенно заменяет строки, добавленные этим же pull request, до попадания работы в целевую ветку. Не учитывайте правки только форматирования, сгенерированные файлы, обновления vendor-зависимостей и намеренное расширение области задачи.
Всегда ли revert означает, что AI-ассистент ошибся?
Нет. Revert показывает, что код попал в ветку, а затем его пришлось отменить. Обычно это серьёзнее переписывания до merge, но причиной также может быть решение о выпуске, поэтому аудиту нужен код причины.
Как точно выявлять повторно открытые дефекты?
Используйте и историю Git, и трекер задач. Git показывает, что код исчез или изменился, а трекер помогает установить, что дефект приняли, исправили, закрыли, а затем снова открыли из-за того же поведения.
Как лучше всего считать раунды код-ревью?
Раунд начинается, когда ревьюер запрашивает изменения или оставляет нерешённый блокирующий комментарий, и заканчивается, когда автор отправляет ревизию в ответ. Простое число комментариев мало что говорит: полезное ревью может содержать много комментариев в одном раунде.
Достаточно ли надёжны Git patch ID для аудита?
Patch ID хорошо помогает сопоставлять эквивалентные патчи после rebase, поскольку Git игнорирует номера строк и пробелы в обычных режимах сравнения. Но это не механизм проверки семантической эквивалентности. Сопоставляйте такие идентификаторы с путями файлов, самим pull request и результатами ручной выборки.
Нужно ли отслеживать долю принятых AI-подсказок?
Нет. Считайте события инструмента показателем его использования, а не качества результата. Инженер может принять много подсказок и выпустить чистый код, а другой использовать инструмент редко, но создать дорогой поток доработок.
Какие данные репозитория нужны для аудита AI-доработок?
Начните со слитых pull request из одного репозитория за период, когда правила работы оставались стабильными. Исключите массовое форматирование, обновления сгенерированного кода, изменения lock-файлов и миграции, если только не выделяете их в отдельные когорты.
Какая комбинация метрик говорит, что AI-процесс разработки создаёт доработки?
Ищите концентрацию, а не один неудачный pull request: высокую долю переписанного diff, дополнительные раунды ревью и повторно открытые дефекты в одной области кода или процесса. Затем изучите небольшую выборку реальных патчей, прежде чем менять промпты, инструменты или состав команды.
Что стартапу делать после обнаружения большого числа AI-доработок?
Меняйте по одному рабочему правилу за раз: требуйте тест до генерации в рискованной области, сужайте формулировку задачи, добавляйте архитектурное ограничение или направляйте определённые изменения к более опытному ревьюеру. Повторное измерение после изменения важнее спора о том, хорош ассистент или плох.


