10–15 багов в месяц превратились в 200–300 — а кому их фиксить?

10–15 багов в месяц превратились в 200–300 — а кому их фиксить? - 1

Фиксить рутинные баги — это отстой. Никто в здравом уме не просыпается с мыслью: «О, как же я хочу сегодня весь день ковырять чужой легаси-код, чтобы понять, почему кнопка в IE11 уезжает на три пикселя влево!»

Так-то мы хотим пилить фичи, строить архитектуру, пить кофе и приходить на перфоманс-ревью просить зарплату больше.

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

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

Короче, нас засыпало. Если раньше приходило 10–15 в месяц, то стало 10–15 в день. А дальше классическая дилемма:

— Если заставить сеньора месяц разгребать минорные баги, он просто выгорит и уйдёт.

— Нанимать джунов фиксить баги — хорошая идея, но не в этом случае.

— Поручить всё это дело нейросетям — возможно, выстрелить себе в ногу.

А когда мы пропускали шанс выстрелить себе в ногу? Короче, сейчас процесс выглядит так: баг попадает на доску в нашем Кайтене, летит в специальную колонку, там его подхватывает модель, пишет фикс, делает саморевью, делает тесты, собирает Pull Request, второй независимый агент в репозитории делает ещё контрольное ревью, и дальше это попадает на ревью к человеку.

Как это ни странно, за 3 месяца у нас не случилось такого, что фикс бага поломал что-то другое. Но тут надо сказать, что мы фильтруем, что попадает к модели, — и ситуации, когда модель не справилась, — они тоже случаются.

Когда баги только начали сыпаться толпой

Мы сначала очень расстроились.

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

А дальше начинается боль деплоя: мы идём друг за другом, создаётся очередь, нужно постоянно обновлять свой код из мастера, ждать аппрувов от коллег. На простейший баг уходят часы. Из-за этого мелкие, незначительные тикеты просто лежали в бэклоге месяцами.

То есть там было две проблемы:

— Сама рутинность задач.

— И куча промежуточных потерь, пока ждёшь другого человека.

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

«Устал я время тратить, пусть нейронка делает»

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

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

Собственно, взял и скинул. Свои основные задачи он закрыл раньше времени, образовалось окно. И решил поковырять идею на коленке — вытащил концепт из бэклога и за выходные в счёт отгулов собрал MVP.

На MVP гоняли синтетические локальные тесты. Дали модели локальный репозиторий и стали работать с задачами типа просто перечислить и добавить имена футболистов. Смотрим — реально ориентируется в репозитории, понимает контекст и делает.

Тогда мы отобрали пару десятков реальных задач. Взяли самые простые, из серии «даём новичку» — те, над которыми пришедший джун может сидеть весь день, просто чтобы понять, где что лежит в коде. Начали подкармливать ими наш новый процесс.

90% этих задач нейросеть проглотила с первого раза и сделала нормальный PR.

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

Как ограничить ИИ, чтобы он не сломал прод

К тому моменту в компании уже официально внедрялся подход AI First. Правило простое: чего-то не понимаешь — перед тем как дёргать живого человека, спроси нейронку.

Разработчики уже активно использовали AI-ассистентов для экономии времени на погружение в контекст и написание бойлерплейта. Но у нас не было никакой автономной работы моделей — LLM — это инструмент, а не замена разработчику.

Первое, что мы сделали, — отдельный, постоянно пополняемый репозиторий с навыками (инструментами и плагинами для ИИ) и системными промптами. Выдали модели ключи доступа к строго ограниченному списку репозиториев микросервисов. Обновили документацию каждого, если она не была в идеальном порядке.

Внутри каждого лежат файлы с контекстом: архитектурное описание, гайдлайны по код-стайлу, которых мы придерживаемся.

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

Не все задачи попадают к модели. Перед тем как скормить задачу в этот поток, есть ручной фильтр. Тимлид читает карточку без глубокого погружения и, опираясь на опыт, распределяет. Если это понятный баг в рамках одного репозитория — отдаём ИИ-кодеру (наш ИИ-помощник). Если понятно, что баг — это не просто мини-фикс, а проблема, которая аффектит схему базы данных или давно устоявшиеся сложные процессы, либо затрагивает личные данные компаний-клиентов, — задача идёт мимо нейросети в очередь к живым разработчикам для ресерча.

При этом описания багов для ИИ никто специально не меняет и не дописывает — он получает ровно то, что написала служба поддержки (с описанием процесса воспроизведения). Если шаги воспроизведения непонятны, мы просим тестировщика попытаться воспроизвести баг и описать кейсы по-человечески. В итоге к моменту начала конвейера есть достаточно точное техническое задание.

Сам процесс

Так как мы сами делаем канбан-доски, мы собрали для ИИ-кодера отдельную доску с колонками: бэклог, в работе, PR готов и передано на тестирование.

10–15 багов в месяц превратились в 200–300 — а кому их фиксить? - 2

Правда, совсем бесконтрольно магия пока не работает: разработчик всё равно должен запустить процесс для конкретной задачи, проконтролировать и перевести её. Робот работает в режиме Concurrency 1 — берёт строго один инстанс за раз. На один баг уходит около 20–40 минут: он клонирует репо, изучает контекст, пишет код. То есть как раз за день всё разбираем.

Дальше идёт автоматическое составное код-ревью. Сначала ИИ-кодер делает селф-ревью. Затем создаёт Pull Request. Дальше к репозиторию подключаются наши плагины и совершенно отдельная самостоятельная нейронка, которая финально перепроверяет код на уязвимости и архитектурные косяки.

Если на этапе PR падают тесты (CI пайплайны), ИИ не сдаётся: у него есть доступ к логам. Он смотрит, что завалилось, понимает, что не учёл, и возвращается доделывать. Бывает, что на баг уходит несколько таких раундов. Если ни один тест не упал и регресс пройден, код выкатывается на стейдж-сервер.

Только на этом этапе подключаются люди — сразу двое. Живой разработчик просто просматривает готовый код глазами как ревьюер, а тестировщик параллельно проверяет по тест-кейсу, что должно быть и что получилось (и что баг больше не воспроизводится). Если проблема осталась, тестировщик пишет замечание прямо в карточку или PR. AI-кодер подхватывает эту обратную связь, анализирует свой прошлый код и вносит правки. 80–90% задач пролетают этот конвейер с первой итерации.

А чтобы днём десятки мелких PR от робота не устраивали гонку за деплой и не создавали пробки, мешая программистам мерджить важные фичи из мастера, мы сделали ночной автодеплой.

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

Как развивались промпты

  1. Базовый скелет: «You are implementing a task from a Kaiten card» + заголовок/описание карточки + 5 строк инструкций (работать только в репозитории, изменения минимальные, следовать code style и архитектурным гайдлайнам, не запускать вотчеры/серверы).

  2. Дальше добавили контекст карточки, список файлов/встроенных картинок, потому что часто в карточках ТЗ в макетах/файлах/скриншотах, а не в тексте.

  3. В Кайтене задачи иерархичны; контекст эпика нужен, но был риск, что агент начнёт делать родителя вместо непосредственной задачи. Появилась секция связанных карточек описаниями предков + оговорка «это только фон, реализуй задачу из основной карточки, а не из предков».

  4. Описание PR — агент пишет финальное описание PR в файл вне рабочего дерева на русском; если есть PR-шаблон репозитория — использовать его структуру; иначе — короткое тело с «Закрывает карточку…», «Изменения», «Тестирование». Надо было, чтобы отделить тело PR от кода (его не нужно коммитить), стандартизировать описания и соблюдать шаблоны PR целевых репозиториев.

  5. Потом добавили секцию ## Returning to an existing branch (только при followUp): прочитать git log/diff base..HEAD, относиться к прежнему коду как к черновику, чинить/удалять. Это потому, что карточку возвращают из ревью на доработку. Без этого агент игнорировал или дублировал прежнюю работу.

  6. Добавили ## Comments — комментарии самой карточки и предков, с автором и привязкой к источнику. Уточнения и причина возврата карточки чаще всего живут именно в комментариях.

  7. PR’ы падали на CI из-за линта. Дальше линтер + дисциплина PR-шаблона. Найти lint-скрипт (lint:check/lint, предпочесть read-only вариант), прогнать и чинить до зелёного.

  8. Агент уходил чинить чужой lint-долг, раздувал диффы и тратил время/токены. Потом линтер научили гонять только по изменённым/добавленным файлам и чинить ошибки только в них, а не во всём репозитории.

Что не так

Конечно, бывают возвраты. Мы даём ИИ три попытки. Если за три захода он не справился, задача переводится целиком в ручной режим с полноценным расследованием. Иногда нейронка просто зацикливается и выдаёт: «Всё, я не могу поправить, правьте сами». Таких возвратов у нас около 5%, это штучные истории.

Если задача возвращается человеку, возникает вопрос экономики. Обычно считается, что распутывать за нейросетью стоит в полтора-два раза (а то и больше) дороже, потому что нужно вникать в то, что она успела наворотить.

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

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

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

Результат

Раньше баги выжигали нервы сразу двух разработчиков из-за рассинхрона и дёрганий.

Теперь на такие задачи уходит время только одного человека. Нейросеть не страдает от того, что её PR висит без аппрува. Разработчик просто решает, что у него есть полчаса в день только на баги.

Он садится как опытный ментор, открывает список уже созданных нейросетью пулл-реквестов и разом их валидирует. Не пишет код, просто проверяет глазами готовое, жмёт аппрув и идёт делать свои основные задачи. Это однотипная компрессированная работа.

Психологически для команды это стало огромным облегчением.

Залежи минорных багов, которые копились месяцами, наконец-то пропущены через эту автоматическую мясорубку. Бэклог очистился.

Сейчас мы дописываем новый функционал ИИ-кодеру, чтобы спихивать и другую рутину. Например, научили его автоматически верстать. Например, если срочно нужен лендинг, кидаем в карточку ИИ-кодера ссылку на макет в Фигме, он понимает, что нужно делать, забирает дизайн, верстает всё сам и отдаёт тестировщику на визуальную сверку.

Следующий шаг — отдать ИИ рутинные запросы от смежных отделов. Например, маркетинг постоянно приходит с экспериментальными запросами на выгрузку из баз данных. Раньше для этого нужно было идти в Mattermost, писать заявку на девопсов, ждать человека с правами доступа. Скоро ИИ-кодер будет забирать эти тикеты и сам лезть в базу. Права ему выданы на реплику базы, в read-only режиме, с жёсткими лимитами по ресурсам и таймаутам. Права не ко всей БД, а к скрипту, написанному человеком, который вытаскивает из БД чётко указанные колонки, не затрагивая персональные данные и прочее. То есть изначально есть очень узконаправленный канал получения данных, к которому подключена ИИ-модель.

Так что вот, работает же.

Автор: dimaninc

Источник

Оставить комментарий