12 правил code-review с AI: что мы разрешаем команде 42 РМ, а что нет
У меня есть клиент — продуктовая компания, где работают 42 человека. Их команда разработки, состоящая из 12 специалистов, вот уже 9 месяцев активно пользуется AI-помощниками: Claude Code, GitHub Copilot и Cursor. За этот период мы накопили кучу данных по PR-ревью. Представьте, было 412 пулл-реквестов, где AI так или иначе поучаствовал! При этом мы поймали 38 живых багов ещё на стейдже и зафиксировали 7 инцидентов уже на проде. Весь этот опыт не прошёл даром — мы выработали 12 чётких правил. Эти правила сейчас закреплены в виде CODEOWNERS-файла и специальных pre-commit hooks, которые живут прямо в их репозиториях. Хочу поделиться этими правилами, показать примеры реальных ошибок, которые они помогают обнаружить, и, конечно, рассказать про наш стенд, где мы прогоняем весь AI-код перед тем, как он отправится в мердж.
Контекст: 9 месяцев работы и что мы реально собрали
Осенью прошлого года ко мне обратился клиент с понятной проблемой: «Помогите нам наладить процесс разработки, наши программисты слишком много времени тратят на рутинные, простые задачи». Они уже полгода пробовали GitHub Copilot, но без всяких правил, и это привело к двум неприятным инцидентам на проде. Один раз произошла SQL-инъекция прямо в endpoint поиска контрагентов, а в другом случае ошибка в обработчике платежей просто молча проглатывалась. После таких случаев мы собрались и я предложил не ставить крест на AI, а наоборот, ввести структурированный подход — не запрещать его, а чётко обозначить границы использования.
За 9 месяцев мои ребята сделали 412 PR-ов, и в каждом из них как минимум 30% строк кода было написано с помощью AI-помощника. Мы это легко отследили по комментариям в коде и, конечно, по логам Cursor или Claude. Изучив эту внушительную выборку, я смог выделить три самых частых типа ошибок, которые постоянно повторялись. Именно на их основе я и составил наши правила.
Цифры по типам ошибок
Тип 1 — «галлюцинации API»: AI вызывает несуществующий метод библиотеки или возвращает функцию, которой нет в установленной версии. У нас 47 случаев за 9 месяцев, из них 31 поймал линтер ещё до коммита, 14 — pre-commit hook, 2 — code review. Тип 2 — «небезопасный SQL и shell»: конкатенация строк вместо параметров, чтение из субшелл-команды без экранирования. 23 случая, из них 17 поймал semgrep, 6 — ревьюер. Тип 3 — «лень в обработке ошибок»: голый except Exception без логирования, return None вместо raise. 38 случаев, ловятся только людьми и линтерами с кастомными правилами.
Правила 1-4: что AI имеет право писать сам
Все эти правила я разделил на четыре блока, в каждом из которых по три пункта. Первый блок посвящён так называемым «доверенным областям». Здесь AI может работать практически самостоятельно, ему нужен лишь минимальный контроль. Сюда относятся типовые CRUD-операции, написание юнит-тестов по уже готовым шаблонам и, конечно, форматирование или миграция кода.
Вот, например, Правило 1: «AI пишет CRUD по существующей модели». Представьте, у вас есть SQLAlchemy-модель Contract и вам нужны стандартные GET/POST/PUT/DELETE-эндпоинты. AI с этим справится за считанные минуты, и это абсолютно безопасно! Ведь модель уже валидируется, схема пермишенов наследуется, а тесты создаются по давно отработанному шаблону. А вот Правило 2 гласит: «AI пишет юнит-тесты только когда задан pytest-шаблон от человека». Тут важно, чтобы тесты, сгенерированные AI, чётко соответствовали тому контракту, который мы зафиксировали вручную. Иначе AI начинает, как бы это сказать, «улучшать» поведение кода специально под свои же тесты, а это нам совсем не нужно.
Правило 3 и 4 — где AI выручает на нудятине
Ещё одно важное — Правило 3: «AI делает массовые рефакторинги по чёткому промпту». Если нужно, к примеру, переименовать поле user_id в owner_id по всему проекту, это просто идеальная работа для AI. Это невероятно занудно для человека, зато требует точности, и при этом результат потом очень легко проверить через git diff. Могу сказать, что за полгода мы таким образом провели 19 переименований и 4 миграции между фреймворками — взять хотя бы перевод старого django-rest на FastAPI для нашего нового сервиса. И Правило 4: «AI пишет docstrings и комментарии к существующему коду». В этом плане AI просто молодец: он внимательно изучает реализацию и очень хорошо описывает её простым, человеческим языком, почти не допуская ошибок.
Правила 5-8: что AI пишет только под надзором
Переходим ко второму блоку — это наша «серая зона». Здесь AI, конечно, может генерировать код, но только в режиме так называемого «pair programming». Это значит, что разработчик должен сидеть рядом, внимательно проверять каждый шаг и ни в коем случае не оставлять AI без присмотра дольше, чем на 15-20 минут. Что сюда относится? Вся бизнес-логика, включая правила расчётов, обработчики платежей и интеграции с внешними API, а также любая работа с базой данных: SQL-запросы, миграции и оптимизации.
Правило 5 звучит так: «Бизнес-логика, связанная с деньгами, — только pair programming». У клиента на проде есть три важных endpoint'а, которые напрямую отвечают за тарифы и платежи. Да, AI может предлагать варианты кода, но помните: коммит делает только реальный разработчик. И что очень важно, в commit-message мы обязательно ставим специальный тег [pair-with-claude]. Благодаря этому в Git Blame у нас остаётся абсолютно чёткая пометка, какие коммиты требуют особого, пристального внимания во время ревью.
Правило 6 — SQL и работа с данными
Согласно Правилу 6, «любой SQL-запрос, который занимает больше 50 строк, проверяется вручную с помощью EXPLAIN». Знаете, AI с удовольствием генерирует JOIN-ы, которые на первый взгляд выглядят совершенно корректно. Но на деле они могут превратиться в какой-нибудь Hash Join на 200 миллионов строк, который будет сравниваться с небольшим справочником, и это просто катастрофа для производительности. Помню, в феврале был такой случай: AI написал отчёт по контрагентам. На тестовых 1 000 записях он отрабатывал всего за 0,3 секунды, ну просто моментально! А вот на проде, когда записей было уже 280 000, он вдруг начал работать 47 секунд и ещё подвисал на блокировке. В итоге ревьюер сделал EXPLAIN ANALYZE, быстро нашёл недостающий индекс, и мы всё починили буквально за 20 минут.
# pre-commit hook — semgrep + кастомные правила
# .pre-commit-config.yaml
repos:
- repo: https://github.com/returntocorp/semgrep
rev: v1.96.0
hooks:
- id: semgrep
args: ['--config=.semgrep/ai-rules.yaml',
'--error', '--quiet', '--strict']
files: \.(py|sql|js|ts)$
# .semgrep/ai-rules.yaml — наши кастомные правила
rules:
- id: no-fstring-sql
pattern: $DB.execute(f"...{$X}...")
message: "Запрещён f-string в SQL — параметризуй через $1, %s или :name"
languages: [python]
severity: ERROR
- id: no-bare-except
pattern: |
try:
...
except:
...
message: "Запрещён голый except — лови конкретный класс ошибки"
languages: [python]
severity: ERROR
- id: no-eval-exec
pattern-either:
- pattern: eval(...)
- pattern: exec(...)
message: "Запрещён eval/exec в продакшн-коде"
languages: [python]
severity: ERROR
Правило 7: «Миграции базы пишет человек, AI только помогает с шаблоном». В alembic-миграциях у нас был случай, когда AI предложил DROP COLUMN на полном merge-сценарии, и если бы это попало на прод, потеря данных была бы необратимой. С тех пор любая alembic-миграция в этом репозитории помечается @migration-by-human, и без этого CI-бот её блокирует. Правило 8: «Интеграции с внешними API — AI пишет skeleton, но обработчик ошибок и таймауты добавляет человек». Здесь главная боль — AI обожает писать requests.get(url).json() без обработки 502, 504 и таймаутов.
Правила 9-12: что AI трогать категорически нельзя
Третий блок — это наша красная черта, настоящие запреты. Здесь у меня очень жёсткая позиция: к AI я вообще не разрешаю подходить, сам сажусь и внимательно читаю код. В эти зоны попадают такие критически важные вещи, как аутентификация и пермишены, обработка платежей (я имею в виду не бизнес-правила, а именно сами интеграции с банками и эквайрингом), любые операции с персональными данными клиентов, регулируемые 152-ФЗ, и, конечно, работа с production-инцидентами.
Вот Правило 9: «Аутентификация и сессии — никакого AI». Вы только представьте: в прошлом году было больше 12 публичных инцидентов в индустрии, когда код, сгенерированный AI, для проверки токенов умудрялся пропускать просроченные сессии или даже принимал пустой Authorization-header за абсолютно валидный! Мне кажется, это явный перегиб со стороны AI-помощников — они чересчур стремятся «упростить» проверки, а это чревато. И Правило 10: «Загрузка/обработка файлов от пользователей — только человек, и обязательно с песочницей». AI очень любит выдавать что-то вроде pickle.load(uploaded_file), совершенно не задумываясь, что это прямой путь к RCE-вектору.
Правила 11 и 12 — про инциденты и легаси
Правило 11 гласит: «На инциденте production не зовём AI». Помню, как у клиента в марте 'упал' endpoint оплаты. Дежурный разработчик в панике попытался найти решение через Claude. Claude выдал целых три гипотезы, и каждая, надо признать, звучала очень убедительно. Но, увы, ни одна из них не оказалась верной! Настоящая причина крылась в совершенно неожиданной миграции схемы у нашего платёжного провайдера. В итоге мы потеряли целых 40 минут, гоняясь за ложными гипотезами AI, пока я, наконец, не написал: «Закрой Claude и просто читай stack trace». И последнее в этом блоке, Правило 12: «Рефакторинг легаси-кода старше 18 месяцев — только с написания тестов». На старом коде AI очень любит всё «упрощать», из-за чего легко теряются те самые пограничные случаи, которые прошлый разработчик мог добавить когда-то после какой-нибудь серьёзной аварии.
Наш чек-лист на PR-ревью: 11 пунктов
Из всех этих правил родился очень удобный чек-лист. У клиента он сейчас используется как PULL_REQUEST_TEMPLATE.md в каждом репозитории и, что важно, автоматически проверяется нашим CI-ботом ещё до того, как ревью вообще открывается. В нём 11 пунктов, и каждый разработчик должен отметить его либо [x], либо [ ]. Если хоть один пункт остался неотмеченным, CI просто не даст открыть PR. Всё строго!
# PULL_REQUEST_TEMPLATE.md в каждом репозитории
## Контекст
- Задача в Jira: [PROJ-XXXX](https://jira.example.com/...)
- Использовался ли AI-помощник: [ ] нет [ ] частично [ ] >50% кода
- Если AI — какой: [ ] Claude Code [ ] Cursor [ ] Copilot
## Чек-лист перед ревью
- [ ] semgrep + bandit прошли без ошибок
- [ ] нет dynamic SQL, eval, exec, pickle.load
- [ ] нет голых except: Exception
- [ ] нет N+1 запросов в ORM (проверь django-debug-toolbar)
- [ ] миграции идемпотентны и обратимы
- [ ] логирование на ошибках (logger.exception на except)
- [ ] секреты не в коде, только из settings/env
- [ ] юнит-тесты написаны от руки (не AI), покрытие >70%
- [ ] прогнан ai-sandbox: docker compose up, тесты PASS
- [ ] нет дублирования логики с существующими модулями
- [ ] proof of human review: я прочитал каждую строку
Последний пункт — про «proof of human review» — у нас живёт как договорённость, а не как технический контроль. Невозможно автоматически проверить, что разработчик прочитал каждую строку. Но в команде клиента живёт правило: если на ревью находится ошибка, которая «выпала из AI», и автор PR не пометил pair-with-claude, — это считается личной ошибкой автора, а не общей. За полгода это сильно дисциплинировало ребят.
Стенд для тестирования AI-кода: изолированный Docker
Помимо pre-commit hooks и нашего строгого чек-листа, у нас на сервере клиента есть ещё один классный инструмент — отдельный стенд, который мы назвали ai-sandbox. Это по сути полная копия нашего prod-стека, развёрнутая в Docker Compose. Внутри всё как на боевом сервере: Postgres 16, Redis 7, Python 3.12, Node 20, RabbitMQ 3.13 и MinIO для S3-эмуляции. Главное, что данные внутри полностью обезличены. Мы один раз запустили специальный скрипт, который заменил все email, телефоны и имена на синтетические, сгенерированные через библиотеку Faker. А чтобы финансовые суммы выглядели реалистично, мы их умножили на случайный коэффициент от 0,2 до 5.
Вот как это работает: когда разработчик создаёт новую фичу с помощью AI, он обязан перед отправкой PR на ревью прогнать все тесты на ai-sandbox локально. Это делается простой командой docker compose up + pytest. Затем наш CI перепрогоняет то же самое уже в чистом контейнере. На типовой PR уходит где-то 8-15 минут, но за полгода такой подход в 11 случаях помог нам поймать баги, которые при обычной разработке вылезли бы только на стейдже, а это гораздо дольше и дороже. Особенно ценно это для поиска ошибок в подписках и периодических задачах. На ai-sandbox можно, так сказать, «прокрутить» 7 дней всего за 3 секунды, просто подменяя time.time().
Затраты на содержание стенда
Сам стенд ai-sandbox размещён на отдельной VM у клиента. Он довольно мощный: 16 vCPU, 32 ГБ RAM и NVMe 500 ГБ. Это обходится примерно в 8 000 ₽ в месяц на хостинге Selectel. Плюс к этому, я трачу около 4 часов своего времени ежемесячно на его поддержку и обновления — это и новые версии Postgres, и регулярное (раз в квартал) обновление обезличенных данных. По нашей внутренней статистике, один единственный пойманный AI-баг экономит команде от 3 до 8 часов работы разработчика, плюс ещё 1-2 часа QA-инженера. В итоге, за 6 месяцев этот стенд окупил себя просто невероятно — в 7-9 раз!
Результат за 9 месяцев — конкретные цифры
Что было до нас? У клиента еженедельно вылезало 4-5 багов прямо на прод! После того, как мы внедрили все эти правила, количество сократилось до 1-2 в неделю. Да, есть и небольшие минусы: время на code-review увеличилось с 22 минут до 28-31 минуты на один PR. Конечно, это дополнительное время, но поверьте, оно полностью окупается. Зато доля отвергнутых PR заметно упала — с 18% до 6%! Это говорит о том, что разработчики стали учитывать наши правила ещё на стадии написания кода. И самое приятное: скорость доставки новых фич, по замерам Jira, выросла на целых 31% за 6 месяцев! А всё потому, что AI взял на себя всю эту скучную, рутинную работу.
Но что для меня, пожалуй, важнее всего — это то, как изменилось отношение к AI внутри команды клиента. Раньше люди думали: «AI творит магию, а мы просто коммитим». Теперь же установка другая: «AI — это просто инструмент, такой же, как Resharper или git: он помогает, но проверять его всё равно нужно». Один из наших senior-разработчиков на встрече в апреле мне так и сказал: «Я наконец-то перестал бояться, что AI меня заменит. Наоборот, он теперь экономит мне полтора часа в день на рутине, и я могу спокойно заняться архитектурой».
FAQ: что чаще всего спрашивают клиенты
В чём AI-помощники реально ошибаются на корпоративном коде?
По нашим данным, собранным за 9 месяцев работы с командой из 12 разработчиков, три основных типа ошибок ответственны за целых 78% всех инцидентов. Первый — это так называемые «выдуманные API». Представьте, Claude или Copilot пишут вызов метода, которого просто не существует в библиотеке! В результате код, естественно, не компилируется, а в случае Python — вообще падает прямо в рантайме. Второй тип — это небезопасные SQL-запросы, где вместо параметризованных мы видим банальную конкатенацию строк. Такое мы ловили 14 раз на PR-review. И третий — это банальная лень в обработке ошибок: AI с удивительной лёгкостью генерирует try-except Exception без какого-либо логирования, а это значит, что любая боевая ошибка будет просто молча проглочена.
Можно ли вообще доверять AI-помощникам безопасно?
Доверять — нет, использовать с правилами — да. У нас есть три уровня контроля. Первый — список запретов в репозитории клиента (никаких dynamic SQL, никаких eval/exec, никаких чтений секретов из окружения мимо секрет-менеджера). Второй — обязательный набор pre-commit hooks с semgrep и bandit, которые ловят 40-60% типовых проблем ещё до review. Третий — человек-ревьюер на каждый PR, и в AI-проекте это правило ужесточается: ни одна строка от AI не попадает в main без живого человека на ревью.
Сколько у вас занимает code-review PR с AI-кодом против PR от человека?
Да, по нашим замерам у клиента ревью занимает на 15-25% дольше, но это совсем не приговор! В среднем, живые разработчики команды тратят 22 минуты на ревью PR, содержащего около 200 строк изменений. А вот на PR-ах с AI-кодом — 28-31 минуту. Причина тут не в том, что AI пишет хуже. Дело в том, что я просто не могу полагаться на интуицию ревьюера, который мог бы сказать: «Да этот разработчик такого точно бы не написал». С AI-кодом каждый, даже самый необычный, паттерн приходится проверять вручную. Но есть и отличная новость — это окупается: число багов на проде упало с 4-5 в неделю до 1-2.
Чем у вас разработчики пишут — Claude, Copilot или Cursor?
У клиента в команде 12 разработчиков, и после полугодового пилота они разбились на три группы. 5 человек сидят на Claude Code (Sonnet 4.5 → Opus 4.6 → 4.7) — это те, кто пишет на Python и Go большие куски логики и любит разговорный стиль. 4 человека выбрали Cursor с переключением между моделями — они работают с фронтом на React/TypeScript и любят быстрые подсказки прямо в редакторе. А 3 разработчика на 1С остались с GitHub Copilot — он хорошо натренирован на открытом коде, и для типовых паттернов 1С его подсказок хватает.
Как у вас выглядит изолированный стенд для тестирования AI-кода?
На сервере клиента мы развернули отдельную Docker-среду, которую назвали «ai-sandbox». Это по сути точная копия нашего prod-стека: там и Postgres 16, и Redis 7, и Python 3.12, и Node 20. Важный момент — данные в ней полностью обезличены; мы прошлись скриптом-санитайзером и заменили все email, телефоны и имена на синтетические, сгенерированные библиотекой Faker. Теперь, когда разработчик создаёт фичу с помощью AI, он обязан перед отправкой PR на ревью прогнать все тесты на ai-sandbox и приложить логи. Это занимает где-то 8-15 минут, но за полгода мы благодаря этому поймали 11 багов, которые при обычном подходе вылезли бы только на стейдже.
Итог
Так что, если говорить об AI-помощнике в корпоративном проекте, то это не вопрос «разрешать или нет». Главный вопрос — «как правильно описать границы его использования». Наши 12 правил, подробный чек-лист для PR и выделенный стенд ai-sandbox принесли клиенту потрясающие результаты: минус 70% багов на проде и плюс 31% к скорости доставки фич. А стоит это удовольствие всего 8 000 ₽ за хостинг в месяц и 4 часа моего времени. Зато окупается всё это за каких-то 4-6 недель за счёт колоссальной экономии на инцидентах.
Похожая задача в вашей компании?
Расскажите, что у вас сейчас — пришлю план работ и оценку в течение рабочего дня.
Написать в Telegram или +7 903 729-62-41
Семёнов Е.С., руководитель ITfresh
