Почему ошибки в коде, сгенерированном GitHub Copilot, так трудно привязать к источнику

У встроенного автодополнения профиль рисков не такой, как у агента, который переписывает файлы. Каждая подсказка достаточно мала, чтобы казаться проверяемой, поэтому вы просматриваете её за секунду и идёте дальше. Эта секунда внимания честна по отношению к строке перед вами и слепа к системе вокруг неё.

Как следствие, когда что-то ломается, поиск виновного изменения превращается в мучение. Нет одного подозрительного коммита — есть длинный хвост принятых автодополнений, каждое из которых в своё время выглядело правильным.

Три вида дрейфа, за которыми стоит следить

Дрейф соглашений

  • Автодополнения следуют шаблонам из обучающих данных и из соседнего кода, а это не всегда те соглашения, которые реально приняты в вашей кодовой базе.

  • Обработка ошибок, проверки на null и логирование постепенно расходятся между модулями.

Дублирование логики

  • Принять сгенерированную вспомогательную функцию быстрее, чем найти уже существующую, поэтому одно и то же правило оказывается реализовано в трёх местах.

  • Потом одну из реализаций исправляют, а две другие — нет.

Правдоподобные значения по умолчанию

  • Предложенное значение по умолчанию, таймаут или порядок сортировки выглядят разумно и при этом не совпадают с правилом, принятым в вашем продукте.

  • Ничего не падает. Просто поведение оказывается не тем, которое кто-либо задумывал.

Все три незаметны при ревью и заметны при работе продукта — именно это и определяет, где должна стоять проверка.

Проверяйте поведение с регулярной периодичностью, а не каждое автодополнение

Проверять каждую принятую подсказку невозможно и бесполезно. Правильная единица — pull request: к этому моменту накопился осмысленный объём изменений, но он всё ещё достаточно мал, чтобы разобраться, если что-то пошло не так.

Проверять нужно не новый код, а поведение, внутри которого этот код живёт. Если pull request затронул модуль счетов, то выставление счетов должно прогоняться от начала до конца, включая пути, о которых автор не подумал, — именно там и прячется правдоподобное значение по умолчанию.

Если установить навык проверки, агент сделает это сам, а не будет ждать, пока человек вспомнит.

Терминал

npm install -g @testsprite/testsprite-cli
testsprite setup

Панель управления закрывает то же самое для тех, кто не хочет ничего устанавливать локально, а полный набор команд описан в репозитории CLI.

Вопрос о регрессиях, который никто не задаёт достаточно рано

Полезный вопрос о кодовой базе, набитой автодополнениями, — не «хорош ли этот код», а «делает ли продукт то же, что и месяц назад». Это разные вопросы, и дрейф ловит только второй.

Чтобы ответить на него, нужен набор проверок, появившихся раньше самих изменений, — и именно это команды откладывают. Десять сценариев, записанных на первой неделе, стоят больше сотни, написанной после первого инцидента, потому что только те первые десять были определены до того, как кто-либо узнал, какие из них окажутся важны.

Привычка ревью, которая масштабируется

Просматривать каждое автодополнение невозможно, а не просматривать ни одного — верный способ накопить дрейф. Работает другая привычка: проверять по категориям, а не построчно.

Если автодополнение добавляет ветку обработки ошибки, убедитесь, что она согласуется с тем, как обрабатывает ошибки остальной модуль: расхождения здесь встречаются чаще всего и потом мучительнее всего распутываются. Если оно добавляет значение по умолчанию, спросите, откуда это значение взялось: правдоподобное умолчание — самый тихий способ изменить поведение. Если оно добавляет вспомогательную функцию, потратьте десять секунд на поиск уже существующей: именно дублирование логики делает будущее исправление неполным.

Каждая из этих трёх проверок занимает несколько секунд и отлавливает большую часть того, что накапливается. Всё остальное надёжнее поймать, прогоняя продукт, а не читая его.

Сделайте проверку автоматической

Дрейф идёт постепенно, поэтому проверка должна быть скучно регулярной. Всё, что зависит от того, вспомнит ли кто-нибудь, будет пропущено ровно на той загруженной неделе, когда принимается больше всего автодополнений.

Если пайплайн принадлежит другой команде, то GitHub App — путь наименьшего сопротивления: это вебхук, он ничего не меняет в вашем репозитории и срабатывает, когда ваша сборка сообщает о выкладке новой версии. Если же вы хотите видеть проверку прямо в репозитории, то шаг GitHub Actions сделает то же самое.

Как поймать дрейф, не читая каждое автодополнение

Просмотреть каждую принятую подсказку невозможно, поэтому TestSprite вместо этого проверяет поведение, внутри которого живёт код. Покрытие генерируется из вашего продукта, запускается на каждом pull request и отвечает на вопрос, который важен для кодовой базы, набитой автодополнениями: делает ли она то же, что и месяц назад.

Так все три вида дрейфа ловятся напрямую. Правдоподобное значение по умолчанию, изменившее порядок сортировки, проявляется как сценарий, который ведёт себя иначе. Дублированная логика проявляется, когда одну копию исправили, а другую — нет. Дрейф соглашений в обработке ошибок проявляется как путь, который теперь падает молча.

Проверка работает там же, где изменение, — комментарием к pull request или проверкой коммита, — поэтому регрессия указывает на одну порцию автодополнений, а не на целый квартал накопленных.

Хуже ли код, сгенерированный Copilot, чем написанный вручную?

В большинстве случаев — не в пересчёте на строку. Риск связан с объёмом: за час в репозиторий попадает больше кода, чем рассчитан выдерживать процесс ревью, поэтому при той же доле дефектов их больше проскакивает наружу.

Решит ли проблему более строгое ревью?

Отчасти — и при этом оно противоречит самой причине, по которой люди вообще пользуются автодополнением. Перенос проверки с чтения кода на проверку поведения сохраняет скорость и возвращает страховочную сетку.

А как насчёт тестов, которые пишет Copilot?

Полезны для покрытия, слабы как независимая проверка. Тест, сгенерированный из тех же предположений, что и код, согласуется с кодом по построению.

Сколько сценариев покрывать в первую очередь?

Начните с той горстки, которую вы показали бы клиенту на демонстрации, плюс любые пути, где задействованы деньги или права доступа. Расширяйте набор от инцидентов, а не от целевого показателя покрытия.

Нужен ли для этого доступ к репозиторию?

Проверка выполняется на развёрнутом приложении через его интерфейс. Доступ к репозиторию нужен только для того, чтобы публиковать результаты в pull request и коммиты.

Коротко

Ошибка — это дрейф, а не отдельная подсказка.

Ошибки в коде, сгенерированном GitHub Copilot, накапливаются во множестве мелких принятых автодополнений. Проверяйте поведение на уровне pull request, а не отдельной подсказки, определяйте сценарии до того, как они понадобятся, и держите проверку в автоматическом режиме.