1. Введение: что журналы коммитов больше не могут объяснить
После внедрения инструментов AI-кодинга в команде код начинает расти быстрее, но процесс его создания становится ещё менее прозрачным. Раньше беглого просмотра журнала коммитов было достаточно, чтобы примерно понять, кто над чем работал и сколько времени это заняло. Теперь даже по тому же журналу невозможно определить, был ли результат получен после часа размышлений человека или создан агентом за три минуты. Репозиторий не изменился, но происходящее внутри него стало невидимым.
Мы поставили перед собой две цели. Во-первых, даже если код был создан агентом, человек, присоединившийся к проекту позже, должен иметь возможность получить только репозиторий, сразу понять его и продолжить работу. Во-вторых, независимо от того, какому проекту принадлежит репозиторий, сразу после его регистрации для анализа должны быть доступны метрики и тренды без какой-либо дополнительной настройки. Эти две цели определили все последующие решения.
2. Что следует подсчитывать?
2.1 Отчётность, а не обнаружение
Сначала мы попытались анализировать изменения в коде и определять части, написанные AI, но вскоре отказались от этого подхода. Коммит содержит автора, временную отметку, изменения и сообщение, но в нём нет места для записи того, «как была создана эта строка». Код, набросанный AI, а затем доработанный и закоммиченный человеком, побайтно идентичен коду, набранному вручную. Проблема не в низкой точности обнаружения: в данных просто нет объекта, который можно было бы обнаружить.
Поэтому мы отказались от обнаружения и решили вместо этого собирать отчёты. Мы учитываем только то, что зафиксировал инструмент или о чём человек явно сообщил. Взамен мы намеренно назвали эту метрику «отчётный AI», а не «доля использования AI». Это поднимает вопрос о том, что происходит, когда люди не сообщают об использовании AI, но мы решили рассматривать это не как недостаток, а как ещё одну метрику. Сам факт низкой доли отчётности является сигналом того, что установленное соглашение не соблюдается.
2.2 Использовать сессии, а не коммиты, в качестве единицы измерения
Ещё более важным было решение о том, что считать одной единицей работы. Работа с агентом не заканчивается за один шаг. Вы даёте инструкции, проверяете результат, просите внести изменения и получаете его снова. В итоге процесс оказывается разделённым между тремя или четырьмя коммитами.
Если считать единицей коммит, остаётся лишь факт, что «агент создал три коммита». На самом деле мы хотим знать: «сколько попыток потребовалось, чтобы завершить эту работу?» Задача, выполненная с одной попытки, и задача, которую пять раз откатывали, рассказывают совершенно разные истории, даже если обе состоят из трёх коммитов. Поэтому мы решили включать в коммиты идентификатор сессии и объединять коммиты одной сессии в одну единицу работы.
|
Единица наблюдения |
На какие вопросы она может ответить |
|---|---|
|
Коммит |
Сколько коммитов было создано с участием агента? |
|
Сессия |
Сколько попыток потребовалось для завершения единицы работы? / Какие задачи включали много откатов? |
3. Выбранный нами подход
3.1 Использовать сообщения коммитов как интерфейс
Если вы не понимаете код, написанный человеком, можно спросить его об этом. С кодом, написанным агентом, так поступить нельзя. После завершения сессии её контекст исчезает, и даже если позже снова вызвать тот же инструмент, он не сможет объяснить, почему в прошлый раз принял такое решение. Поэтому чем активнее агент участвует в создании кода, тем важнее, чтобы его объяснение сохранялось рядом с самим кодом. Только так человек, присоединившийся к проекту позже, сможет понять его, имея лишь репозиторий.
Мы могли бы накапливать записи в отдельном репозитории или получать их через API инструмента, но выбрали сообщения коммитов. Коммиты сохраняются независимо от используемого инструмента, поэтому этот подход не зависит от конкретного инструмента; записи перемещаются вместе с кодом и сохраняются при репликации или зеркалировании; а поскольку исходный текст сохраняется без изменений, его можно перечитать позже, даже если правила интерпретации изменятся. Для формата мы без изменений использовали существующее соглашение Git о trailer-полях.
[SPEC-008] feat(member): 세션에서 사용자 신원을 읽도록 변경
X-Agent: dev
X-Agent-Session: 3ae7dbc4
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Здесь была одна ловушка. co-authored-by изначально является соглашением для обозначения совместной работы людей, поэтому его использование без изменений также классифицировало бы коммиты, созданные двумя совместно работающими людьми, как коммиты AI. Это распространённая проблема при переиспользовании широко применяемого соглашения, поэтому мы признавали что-либо агентом только в том случае, если домен его электронной почты принадлежал известному инструменту.
3.2 Разместить соглашение в инструменте, а не в людях
Это была самая неожиданная часть. Соглашения о коммитах обычно не соблюдаются последовательно. Даже если их документируют и объясняют во время адаптации, через несколько недель они забываются. Так обычно происходит с правилами, соблюдение которых зависит от внимательности людей. Однако агенты каждый раз читают файл с соглашениями проекта и точно им следуют. Они не полагаются на память: поскольку при каждой работе перечитывают этот файл, правила не забываются со временем.
## 커밋 메시지 규약
[<백로그>] <type>(<scope>): <제목>
X-Agent: <역할> # dev · review · test · fix · chore 중 하나
X-Agent-Session: <식별자> # 세션 내내 같은 값을 쓴다
- 제목 한 줄이면 끝이다. 본문을 쓰지 않는다.
- 한 커밋에 역할 두 개를 담지 않는다.
Сама концепция соглашения о коммитах не нова. Все знали, что оно важно, но его просто не соблюдали. Изменилось не соглашение, а тот, кто обеспечивает его соблюдение. Поэтому важно, где размещён файл с соглашением. Если хранить его внутри репозитория, изменение соглашения проходит ревью так же, как изменение кода, и сохраняется в истории версий; если код меняется, а соглашение остаётся прежним, это расхождение становится видимым в том же коммите. Если бы файл находился в wiki, он мог бы потерять синхронизацию с кодом, и никто бы этого не заметил.
Однако есть одно правило, которое необходимо соблюдать: не добавляйте trailer для отчётности в коммиты, созданные непосредственно людьми. Если люди начнут добавлять его для удобства, работа людей и работа агентов смешаются, и в этот момент вопросы, на которые должны были отвечать эти данные, полностью утратят смысл. Создавая соглашение, нужно решить не только, что записывать, но и чего не записывать.
Ограничение числа ролей пятью и разрешение только одной роли на коммит основаны на том же принципе. Если вам хочется указать две роли, это сигнал к разделению коммита. Значения, не включённые в список, не отбрасываются: они учитываются как есть. Сам факт несоблюдения соглашения является сигналом, который стоит наблюдать, а его доля становится метрикой соблюдения соглашения.
3.3 Выводить классификации вместо их хранения
Завершив реализацию, мы открыли дашборд и обнаружили, что числа намного меньше ожидаемых. Причина была не в данных, а в проектном решении. Сущности коммитов были спроектированы как неизменяемые: после загрузки их значения нельзя было изменить. Однако результат классификации мы хранили в поле этой сущности. Поэтому у коммитов, загруженных до добавления парсера, это поле навсегда оставалось пустым.
|
Подход |
Описание |
Проблема |
|---|---|---|
|
Пакетная миграция |
Просканировать прошлые строки и заполнить поле |
Необходимо запускать заново при каждом изменении правил |
|
Разрешить обновление поля |
Сделать сущность изменяемой |
Отказаться от неизменяемой архитектуры ради одной классификации |
|
Выводить при агрегации |
Каждый раз классифицировать данные, не сохраняя результат |
Затраты на парсинг возникают при каждой агрегации данных |
Мы выбрали третий вариант. Правила классификации продолжат меняться, а структура, требующая каждый раз переносить изменения на прошлые данные, не выдержит долгосрочной эксплуатации. Если результат выводится, одна повторная агрегация применяет новые правила и к прошлым данным. Когда мы повторно проверили затраты на парсинг, которых опасались, оказалось, что они не оказывают существенного влияния на время агрегации. Затраты, которые мы надеялись сэкономить за счёт хранения результата, изначально не стоило экономить.
4. Результаты
4.1 При добавлении нового репозитория всё продолжает работать без изменений
Приведённые выше решения могут выглядеть независимыми, но все они сходятся вокруг одного вопроса: что ещё нужно сделать, когда новый репозиторий регистрируется как цель анализа?
|
Решение |
Следовательно, в новом репозитории |
|---|---|
|
Оставить запись в сообщении коммита |
Добавление только репозитория переносит запись вместе с ним |
|
Выводить классификацию вместо её хранения |
Сразу после регистрации агрегация обращается к прошлым коммитам и включает их |
|
Хранить список поставщиков и типов как данные политики |
Сразу запускать с настройками по умолчанию и при необходимости переопределять их |
В результате при регистрации нового репозитория не требуется ничего специально подготавливать. Если команда придерживалась соглашений о коммитах, метрики появляются с момента регистрации, а прошлые коммиты также рассчитываются, поэтому тенденцию можно сразу увидеть на первом экране. И наоборот, если бы записи накапливались в отдельном репозитории, для каждого репозитория потребовалась бы интеграция, а если бы сохранялись результаты определения, история вновь подключённого репозитория навсегда оставалась бы пустой.
4.2 Сделать так, чтобы экран показывал причину появления значения 0
Нам также пришлось решить, скрывать ли из списка участников с нулевым зарегистрированным использованием ИИ или показывать их как есть. Если скрыть их, экран будет выглядеть чище. Однако после скрытия случаи «не использует» и «использует, но не сообщает об этом» выглядят на экране одинаково. Ранее мы решили использовать сам коэффициент отчётности как метрику, но если экран смешивает эти два случая, это решение теряет смысл. Поэтому вместо того, чтобы скрывать нулевые значения, мы сделали так, чтобы экран напрямую указывал причину, по которой значение равно нулю.
No AI use was reported in this period.
Commits alone cannot tell not-used from not-declared.
Мы применили тот же принцип, когда метрику не удавалось загрузить. Если запрос завершается ошибкой, мы не отображаем 0, а указываем, что значение не удалось загрузить. Термометр, показывающий 36,5, и термометр, шкала которого остановилась из-за неисправности, описывают совершенно разные состояния, но по одному числу их различить невозможно. Сбой запроса и значение 0 аналогичны.
5. Заключение
Подключение агента к команде отличается от простого использования ещё одного инструмента. Это означает добавление нового участника в процесс разработки, а значит, требует создания нового места для записи того, что этот участник сделал. Большая часть работы ушла на решение, что записывать, а что не записывать, а не на разбор или агрегацию данных.
Самым практичным открытием стало то, что соглашения можно встраивать в инструменты, а не оставлять на усмотрение людей. Люди давно говорят о важности соглашений, но эта идея всегда опиралась на личную добросовестность, и в результате соглашения обычно не соблюдались. По сути, фокус сместился с самих соглашений на место, где обеспечивается их соблюдение. Однако единственное правило, которому люди по-прежнему должны следовать, — не смешивать работу человека с работой агента.
В конечном счёте мы хотели простого: даже если код создан агентом, человек, который придёт позже, должен иметь возможность сразу продолжить работу, получив только репозиторий, а в момент подключения этого репозитория к анализу результаты и тенденции должны отображаться без какой-либо подготовки. Чтобы это стало возможным, пояснения должны находиться рядом с кодом, соглашения — внутри репозитория, а интерпретацию должно быть возможно воспроизвести в любой момент за пределами репозитория.
Ссылки
Git – git-interpret-trailers — https://git-scm.com/docs/git-interpret-trailers
GitHub Docs – Создание коммита с несколькими авторами — https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors
Conventional Commits 1.0.0 — https://www.conventionalcommits.org/ko/v1.0.0/
toffeeman