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

Фиксить рутинные баги — это отстой. Никто в здравом уме не просыпается с мыслью: «О, как же я хочу сегодня весь день ковырять чужой легаси-код, чтобы понять, почему кнопка в IE11 уезжает на три пикселя влево!»
Так-то мы хотим пилить фичи, строить архитектуру, пить кофе и приходить на перфоманс-ревью просить зарплату больше.
В наш сегмент прилетало ну, может, пара десятков багов в месяц. Мы слегка размазывали их по спринтам. Некоторые особо скучные и мелкие не сдвигались годами. Но примерно 4 месяца назад поменялась система атрибуции багов, и они стали уноситься не в подразделение, которое писало изначальный код, а к тем, кто этот код эксплуатирует.
Бизнес за это время сильно вырос, и начали всплывать редкие с трудом воспроизводимые баги краевых случаев.
Короче, нас засыпало. Если раньше приходило 10–15 в месяц, то стало 10–15 в день. А дальше классическая дилемма:
— Если заставить сеньора месяц разгребать минорные баги, он просто выгорит и уйдёт.
— Нанимать джунов фиксить баги — хорошая идея, но не в этом случае.
— Поручить всё это дело нейросетям — возможно, выстрелить себе в ногу.
А когда мы пропускали шанс выстрелить себе в ногу? Короче, сейчас процесс выглядит так: баг попадает на доску в нашем Кайтене, летит в специальную колонку, там его подхватывает модель, пишет фикс, делает саморевью, делает тесты, собирает Pull Request, второй независимый агент в репозитории делает ещё контрольное ревью, и дальше это попадает на ревью к человеку.
Как это ни странно, за 3 месяца у нас не случилось такого, что фикс бага поломал что-то другое. Но тут надо сказать, что мы фильтруем, что попадает к модели, — и ситуации, когда модель не справилась, — они тоже случаются.
Когда баги только начали сыпаться толпой
Мы сначала очень расстроились.
Разработчиков у нас в подразделении много, они сильные, задачи архитектурно сложные. Отвлекаться на рутину, где что-то на пиксель уехало, цвет не тот или закралась орфографическая ошибка в тексте, — физически и морально больно и скучно. Любой, даже самый микроскопический, фикс требует времени: нужно вынырнуть из контекста, найти нужное место в огромной кодовой базе микросервисов, проанализировать проблему, написать код, создать PR.
А дальше начинается боль деплоя: мы идём друг за другом, создаётся очередь, нужно постоянно обновлять свой код из мастера, ждать аппрувов от коллег. На простейший баг уходят часы. Из-за этого мелкие, незначительные тикеты просто лежали в бэклоге месяцами.
То есть там было две проблемы:
— Сама рутинность задач.
— И куча промежуточных потерь, пока ждёшь другого человека.
Даже если просто схлопнуть процесс до отсутствия зависимостей от кого-то ещё — уже пойдёт веселее. Но мы хотели решить обе задачи.
«Устал я время тратить, пусть нейронка делает»
Идея автоматизировать эту тоску жила у нас в бэклоге месяцы, если не годы.
Постоянно не хватало ресурсов, всегда находились более срочные задачи. Триггером стал обычный бытовой разговор. Мы общались на отвлечённые темы, и один из наших программистов в сердцах выдал, что невероятно устал тратить время на эту рутину и круто было бы скинуть её на нейронку.
Собственно, взял и скинул. Свои основные задачи он закрыл раньше времени, образовалось окно. И решил поковырять идею на коленке — вытащил концепт из бэклога и за выходные в счёт отгулов собрал MVP.
На MVP гоняли синтетические локальные тесты. Дали модели локальный репозиторий и стали работать с задачами типа просто перечислить и добавить имена футболистов. Смотрим — реально ориентируется в репозитории, понимает контекст и делает.
Тогда мы отобрали пару десятков реальных задач. Взяли самые простые, из серии «даём новичку» — те, над которыми пришедший джун может сидеть весь день, просто чтобы понять, где что лежит в коде. Начали подкармливать ими наш новый процесс.
90% этих задач нейросеть проглотила с первого раза и сделала нормальный PR.
10%, конечно, получились ужасно. Это из простых. Но, во-первых, модели тогда были старыми и не очень умными, а во-вторых, у нас не было обвязки из правильных промптов, которые очень сильно поднимают эффективность в результате. Началась эволюция наших промптов, и рядом шла эволюция моделей.
Как ограничить ИИ, чтобы он не сломал прод
К тому моменту в компании уже официально внедрялся подход AI First. Правило простое: чего-то не понимаешь — перед тем как дёргать живого человека, спроси нейронку.
Разработчики уже активно использовали AI-ассистентов для экономии времени на погружение в контекст и написание бойлерплейта. Но у нас не было никакой автономной работы моделей — LLM — это инструмент, а не замена разработчику.
Первое, что мы сделали, — отдельный, постоянно пополняемый репозиторий с навыками (инструментами и плагинами для ИИ) и системными промптами. Выдали модели ключи доступа к строго ограниченному списку репозиториев микросервисов. Обновили документацию каждого, если она не была в идеальном порядке.
Внутри каждого лежат файлы с контекстом: архитектурное описание, гайдлайны по код-стайлу, которых мы придерживаемся.
Промпты менялись постоянно. Сначала модель пыталась лезть в дебри, куда не следует. Нам пришлось жёстко связать ей руки инструкциями: делай чётко то, что описано; не делай того, чего не требуется; не предлагай того, о чём не просят; за минимальное время и минимальными средствами почини баг и не рыпайся.
Не все задачи попадают к модели. Перед тем как скормить задачу в этот поток, есть ручной фильтр. Тимлид читает карточку без глубокого погружения и, опираясь на опыт, распределяет. Если это понятный баг в рамках одного репозитория — отдаём ИИ-кодеру (наш ИИ-помощник). Если понятно, что баг — это не просто мини-фикс, а проблема, которая аффектит схему базы данных или давно устоявшиеся сложные процессы, либо затрагивает личные данные компаний-клиентов, — задача идёт мимо нейросети в очередь к живым разработчикам для ресерча.
При этом описания багов для ИИ никто специально не меняет и не дописывает — он получает ровно то, что написала служба поддержки (с описанием процесса воспроизведения). Если шаги воспроизведения непонятны, мы просим тестировщика попытаться воспроизвести баг и описать кейсы по-человечески. В итоге к моменту начала конвейера есть достаточно точное техническое задание.
Сам процесс
Так как мы сами делаем канбан-доски, мы собрали для ИИ-кодера отдельную доску с колонками: бэклог, в работе, PR готов и передано на тестирование.

Правда, совсем бесконтрольно магия пока не работает: разработчик всё равно должен запустить процесс для конкретной задачи, проконтролировать и перевести её. Робот работает в режиме Concurrency 1 — берёт строго один инстанс за раз. На один баг уходит около 20–40 минут: он клонирует репо, изучает контекст, пишет код. То есть как раз за день всё разбираем.
Дальше идёт автоматическое составное код-ревью. Сначала ИИ-кодер делает селф-ревью. Затем создаёт Pull Request. Дальше к репозиторию подключаются наши плагины и совершенно отдельная самостоятельная нейронка, которая финально перепроверяет код на уязвимости и архитектурные косяки.
Если на этапе PR падают тесты (CI пайплайны), ИИ не сдаётся: у него есть доступ к логам. Он смотрит, что завалилось, понимает, что не учёл, и возвращается доделывать. Бывает, что на баг уходит несколько таких раундов. Если ни один тест не упал и регресс пройден, код выкатывается на стейдж-сервер.
Только на этом этапе подключаются люди — сразу двое. Живой разработчик просто просматривает готовый код глазами как ревьюер, а тестировщик параллельно проверяет по тест-кейсу, что должно быть и что получилось (и что баг больше не воспроизводится). Если проблема осталась, тестировщик пишет замечание прямо в карточку или PR. AI-кодер подхватывает эту обратную связь, анализирует свой прошлый код и вносит правки. 80–90% задач пролетают этот конвейер с первой итерации.
А чтобы днём десятки мелких PR от робота не устраивали гонку за деплой и не создавали пробки, мешая программистам мерджить важные фичи из мастера, мы сделали ночной автодеплой.
Робот копит аппрувленные задачи и выкатывает их ночью, пока все спят. «Папа, не деплой в пятницу, тебя ждут дома» — такого нет.
Как развивались промпты
-
Базовый скелет: «You are implementing a task from a Kaiten card» + заголовок/описание карточки + 5 строк инструкций (работать только в репозитории, изменения минимальные, следовать code style и архитектурным гайдлайнам, не запускать вотчеры/серверы).
-
Дальше добавили контекст карточки, список файлов/встроенных картинок, потому что часто в карточках ТЗ в макетах/файлах/скриншотах, а не в тексте.
-
В Кайтене задачи иерархичны; контекст эпика нужен, но был риск, что агент начнёт делать родителя вместо непосредственной задачи. Появилась секция связанных карточек описаниями предков + оговорка «это только фон, реализуй задачу из основной карточки, а не из предков».
-
Описание PR — агент пишет финальное описание PR в файл вне рабочего дерева на русском; если есть PR-шаблон репозитория — использовать его структуру; иначе — короткое тело с «Закрывает карточку…», «Изменения», «Тестирование». Надо было, чтобы отделить тело PR от кода (его не нужно коммитить), стандартизировать описания и соблюдать шаблоны PR целевых репозиториев.
-
Потом добавили секцию ## Returning to an existing branch (только при followUp): прочитать git log/diff base..HEAD, относиться к прежнему коду как к черновику, чинить/удалять. Это потому, что карточку возвращают из ревью на доработку. Без этого агент игнорировал или дублировал прежнюю работу.
-
Добавили ## Comments — комментарии самой карточки и предков, с автором и привязкой к источнику. Уточнения и причина возврата карточки чаще всего живут именно в комментариях.
-
PR’ы падали на CI из-за линта. Дальше линтер + дисциплина PR-шаблона. Найти lint-скрипт (lint:check/lint, предпочесть read-only вариант), прогнать и чинить до зелёного.
-
Агент уходил чинить чужой lint-долг, раздувал диффы и тратил время/токены. Потом линтер научили гонять только по изменённым/добавленным файлам и чинить ошибки только в них, а не во всём репозитории.
Что не так
Конечно, бывают возвраты. Мы даём ИИ три попытки. Если за три захода он не справился, задача переводится целиком в ручной режим с полноценным расследованием. Иногда нейронка просто зацикливается и выдаёт: «Всё, я не могу поправить, правьте сами». Таких возвратов у нас около 5%, это штучные истории.
Если задача возвращается человеку, возникает вопрос экономики. Обычно считается, что распутывать за нейросетью стоит в полтора-два раза (а то и больше) дороже, потому что нужно вникать в то, что она успела наворотить.
У нас есть жёсткий совет для таких случаев: разработчик должен полностью абстрагироваться и вообще не смотреть в тот код, который писала модель. Раз с трёх попыток не вышло — значит, она пошла по ложному следу. Дешевле просто разобраться с нуля.
За несколько месяцев потоковой работы — а робот чинит десятки багов в сутки — мы ни разу ничего не уронили на проде и не сломали. Нас спасает полный регресс-набор автотестов перед мерджем в мастер. Если падает хоть один тест, задача не передаётся на тестирование и возвращается. Максимум, что у нас было: на стейдже с тестовыми данными баг казался починенным, а на проде с реальным контекстом всплывал снова.
Мы ещё ждали, что разработчики будут злиться на спагетти-код от моделей, но такое если и было, то только на самых ранних этапах, пока не появился нормальный harness из наших промптов, пока обновляли документацию по стилям и архитектуре и пока модели не поумнели. Код нормальный, людей не бесит, наши внутренние требования чётко соблюдаются.
Результат
Раньше баги выжигали нервы сразу двух разработчиков из-за рассинхрона и дёрганий.
Теперь на такие задачи уходит время только одного человека. Нейросеть не страдает от того, что её PR висит без аппрува. Разработчик просто решает, что у него есть полчаса в день только на баги.
Он садится как опытный ментор, открывает список уже созданных нейросетью пулл-реквестов и разом их валидирует. Не пишет код, просто проверяет глазами готовое, жмёт аппрув и идёт делать свои основные задачи. Это однотипная компрессированная работа.
Психологически для команды это стало огромным облегчением.
Залежи минорных багов, которые копились месяцами, наконец-то пропущены через эту автоматическую мясорубку. Бэклог очистился.
Сейчас мы дописываем новый функционал ИИ-кодеру, чтобы спихивать и другую рутину. Например, научили его автоматически верстать. Например, если срочно нужен лендинг, кидаем в карточку ИИ-кодера ссылку на макет в Фигме, он понимает, что нужно делать, забирает дизайн, верстает всё сам и отдаёт тестировщику на визуальную сверку.
Следующий шаг — отдать ИИ рутинные запросы от смежных отделов. Например, маркетинг постоянно приходит с экспериментальными запросами на выгрузку из баз данных. Раньше для этого нужно было идти в Mattermost, писать заявку на девопсов, ждать человека с правами доступа. Скоро ИИ-кодер будет забирать эти тикеты и сам лезть в базу. Права ему выданы на реплику базы, в read-only режиме, с жёсткими лимитами по ресурсам и таймаутам. Права не ко всей БД, а к скрипту, написанному человеком, который вытаскивает из БД чётко указанные колонки, не затрагивая персональные данные и прочее. То есть изначально есть очень узконаправленный канал получения данных, к которому подключена ИИ-модель.
Так что вот, работает же.
Автор: dimaninc

