AI Code Review Checklist: Correctness, Security, Performance, Readability
Что такое AI code review?
AI code review — это использование языковых моделей для систематического анализа кода по четырём категориям в фиксированном порядке приоритетов: корректность, безопасность, производительность, читаемость — каждая в отдельном промпте, чтобы критические баги не тонули среди замечаний по стилю. Это дополнение к человеческому ревью, а не замена: AI ловит edge cases и проблемы безопасности, которые рецензент пропускает, начиная с поверхности кода.
TL;DR
- -Ревью в фиксированном порядке: Корректность → Безопасность → Производительность → Читаемость; без порядка 80% времени уходит на стиль
- -Баг в бизнес-логике, пойманный до мержа, обходится в 10–30 раз дешевле, чем попавший в продакшн
- -Каждая категория — отдельный промпт для LLM, чтобы находки по безопасности не смешивались с замечаниями по стилю
- -Этап безопасности: инъекции, утечки данных, ошибки авторизации, секреты в логах, уязвимые зависимости
- -Шаблон интеграции в CI: корректность и безопасность — блокирующие проверки на каждом PR
68% дефектов, пропущенных на code review, относятся к логическим ошибкам и edge cases. Не к форматированию, не к naming conventions. Google опубликовал эти данные в исследовании «Modern Code Review: A Case Study at Google» (2018), и с тех пор объём AI-сгенерированного кода вырос, а ревьюеры тратят те же 15-30 минут на PR.
Ниже — как структурировать AI code review по четырём категориям: корректность, безопасность, производительность, читаемость. Приоритет именно в таком порядке. Для каждой категории: чеклист, промпт для LLM, примеры находок. В конце — интеграция в CI pipeline.
Почему порядок категорий имеет значение
Типичный code review начинается с поверхности. Ревьюер замечает неудачное имя переменной, предлагает рефакторинг, обсуждает стиль. На это уходит 80% времени. Логические ошибки и проблемы безопасности остаются незамеченными.
Фиксированный порядок решает эту проблему:
- Correctness — код делает то, что заявлено? Edge cases обработаны?
- Security — нет ли инъекций, утечек данных, проблем с авторизацией?
- Performance — нет ли O(n²) там, где хватит O(n)? Нет лишних аллокаций?
- Readability — понятен ли код через полгода? Соответствует ли conventions проекта?
Каждая следующая категория менее критична. Баг в логике авторизации важнее неудачного имени переменной. LLM отрабатывает каждую категорию отдельным проходом, не смешивая комментарии о безопасности с замечаниями о стиле.
Этап 1: Correctness — логика и edge cases
Самая дорогая категория ошибок. Баг в бизнес-логике, который прошёл review и попал в production, стоит в 10-30 раз дороже, чем баг, пойманный до merge.
Чеклист
- Граничные значения: null, пустая строка, пустой массив, 0, отрицательные числа
- Off-by-one:
<вместо<=, индексы массивов, пагинация - Конкурентный доступ: race conditions при параллельных запросах
- Обработка ошибок: что произойдёт при таймауте, 500-й ошибке, разрыве соединения
- Типы данных: переполнение integer, потеря точности float, неявные приведения
- Контракты: входные данные валидируются? Выходные соответствуют интерфейсу?
- Идемпотентность: безопасен ли повторный вызов?
Промпт для LLM
Проведи code review на корректность. Проверь ТОЛЬКО логические ошибки.
Контекст:
- Язык: {language}
- Назначение функции: {purpose}
- Вызывается из: {caller context}
Проверь каждый пункт:
1. Граничные значения (null, пустые коллекции, 0, отрицательные)
2. Off-by-one ошибки в циклах и условиях
3. Race conditions при параллельном доступе
4. Обработка ошибок (таймауты, сетевые сбои, невалидный ответ)
5. Типы данных (переполнение, потеря точности, неявные приведения)
6. Идемпотентность повторного вызова
Для каждой находки укажи:
- Строку кода
- Что произойдёт при конкретном входе
- Как исправить (одно предложение)
Не комментируй стиль, naming или форматирование.
Пример находки
Функция расчёта скидки:
def calculate_discount(price: float, quantity: int) -> float:
if quantity > 10:
return price * 0.9
if quantity > 50:
return price * 0.8
return price
LLM ловит: второе условие quantity > 50 никогда не выполнится, потому что при quantity > 50 сработает первое условие quantity > 10. Скидка 20% недоступна. Порядок условий нужно развернуть.
Такие ошибки проходят тесты, если тест-кейс для quantity=100 проверяет только наличие скидки, а не её размер.
Этап 2: Security — уязвимости и утечки данных
Безопасность идёт второй, потому что уязвимость в корректно работающем коде опаснее бага: баг виден по ошибкам, уязвимость эксплуатируют молча.
Чеклист
- Инъекции: SQL, NoSQL, command injection, XSS
- Аутентификация: проверяется ли identity на каждом эндпоинте?
- Авторизация: пользователь может получить доступ только к своим данным?
- Секреты: нет ли hardcoded ключей, токенов, паролей?
- Логирование: не попадают ли PII/секреты в логи?
- Десериализация: входные данные проверяются перед парсингом?
- Зависимости: нет ли известных CVE в подключаемых пакетах?
Промпт для LLM
Проведи security review кода. Проверь ТОЛЬКО уязвимости.
Контекст:
- Стек: {stack}
- Этот код доступен: {public API / internal / edge function}
- Аутентификация: {auth method}
Проверь по OWASP Top 10:
1. Injection (SQL, command, XSS) — пользовательский ввод попадает
в запросы или HTML без санитизации?
2. Broken Access Control — можно ли получить чужие данные,
подменив user_id / tenant_id?
3. Cryptographic Failures — секреты в коде, слабое хеширование,
HTTP вместо HTTPS?
4. Security Misconfiguration — CORS *, debug=True, verbose errors?
5. SSRF — можно ли заставить сервер обратиться к внутреннему ресурсу?
Для каждой находки:
- CWE-номер (если применимо)
- Severity: Critical / High / Medium / Low
- Exploit-сценарий в одном предложении
- Fix в одном предложении
Не комментируй производительность, стиль или логику.
Пример находки
Эндпоинт Supabase Edge Function:
const { data } = await supabase
.from('documents')
.select('*')
.eq('id', req.params.id);
return new Response(JSON.stringify(data));
LLM ловит: отсутствует проверка user_id. Любой аутентифицированный пользователь получает документ по ID, даже если он принадлежит другому пользователю. IDOR (Insecure Direct Object Reference, CWE-639). Severity: High.
Фикс: добавить .eq('user_id', user.id) или RLS-политику на уровне базы.
Подробнее о том, как LLM оценивает качество кода автоматически — в статье LLM-as-Judge: автоматический quality gate.
Этап 3: Performance — ресурсы и масштабируемость
Производительность проверяется после корректности и безопасности. Быстрый, но некорректный или уязвимый код бесполезен.
Чеклист
- Сложность алгоритмов: нет ли вложенных циклов по тем же данным?
- N+1 запросов: цикл с запросом к БД внутри
- Лишние аллокации: создание объектов в горячем цикле
- Отсутствие кеширования: одни и те же данные запрашиваются повторно
- Размер ответа: SELECT * вместо нужных полей
- Индексы: фильтрация по неиндексированным полям
- Memory leaks: подписки без отписки, незакрытые соединения
Промпт для LLM
Проведи performance review. Проверь ТОЛЬКО проблемы производительности.
Контекст:
- Ожидаемая нагрузка: {requests per second / dataset size}
- Среда: {serverless / server / edge}
- БД: {database}
Проверь:
1. Алгоритмическая сложность — O(n²) или хуже?
2. N+1 запросов — цикл с запросом внутри?
3. Лишние аллокации — объекты создаются в горячем пути?
4. SELECT * — возвращаются ненужные поля?
5. Отсутствие индексов — WHERE/ORDER BY по неиндексированному полю?
6. Утечки — незакрытые соединения, подписки без отписки?
Для каждой находки:
- Текущая сложность / стоимость
- При каком объеме данных станет проблемой
- Fix в одном предложении
Не комментируй корректность, безопасность или стиль.
Пример находки
Загрузка активности пользователей:
const users = await getActiveUsers(); // 500 users
const activity = [];
for (const user of users) {
const logs = await db.query(
`SELECT * FROM activity_logs WHERE user_id = $1`,
[user.id]
);
activity.push({ user, logs });
}
LLM ловит: классический N+1. При 500 активных пользователях выполняется 501 запрос к базе. На serverless каждый запрос имеет latency 2-5ms — итого 1-2.5 секунды только на запросы. Решение: один запрос с WHERE user_id = ANY($1) и группировка на стороне приложения.
Для edge functions это критично. Статья Circuit Breaker в Deno Edge Functions описывает, как таймауты из-за медленных запросов вызывают каскадные отказы.
Этап 4: Readability — поддерживаемость и conventions
Последняя категория. Замечания по читаемости не должны блокировать merge, если первые три этапа пройдены.
Чеклист
- Naming: имена переменных и функций отражают назначение?
- Размер функций: больше 30-40 строк — кандидат на разбиение
- Комментарии: объясняют «почему», а не «что»
- Dead code: неиспользуемые переменные, недостижимые ветки
- Дублирование: одна и та же логика в нескольких местах
- Conventions: соответствие стилю проекта (не общим best practices)
- Типизация: конкретные типы вместо
any/object
Промпт для LLM
Проведи review на читаемость. Проверь ТОЛЬКО поддерживаемость кода.
Контекст:
- Стиль проекта: {ссылка на conventions или примеры}
- Паттерны: {DI framework, error handling pattern, etc.}
Проверь:
1. Naming — переменные и функции понятны без контекста?
2. Размер — функции длиннее 40 строк?
3. Dead code — неиспользуемые импорты, переменные, ветки?
4. Дублирование — логика повторяется? Есть ли существующий хелпер?
5. Типизация — any/unknown/object вместо конкретных типов?
6. Conventions — код соответствует стилю остального проекта?
Для каждой находки:
- Severity: nit / suggestion / convention-violation
- Одно предложение, что улучшить
Не комментируй логику, безопасность или производительность.
Пример находки
const d = await fetchData(id);
const r = processResult(d);
if (r.s === 'ok') {
await save(r.d);
}
LLM ловит: однобуквенные переменные d, r, свойства s и d без контекста. Через полгода невозможно понять, что r.s — это статус, а r.d — обработанные данные. Severity: suggestion.
Объединение в один review pipeline
Четыре прохода дают много замечаний. Нужна структура для финального отчёта.
Формат вывода
## Code Review Summary
### Correctness (блокирующие)
- [C1] line 42: quantity > 50 недостижим — развернуть порядок условий
- [C2] line 87: нет обработки null response от API
### Security (блокирующие)
- [S1] line 15: IDOR — отсутствует проверка user_id (CWE-639, High)
### Performance (предупреждения)
- [P1] line 23-28: N+1 запросов, заменить на batch query
### Readability (рекомендации)
- [R1] line 5: однобуквенные переменные — переименовать
- [R2] line 33: dead code — убрать неиспользуемый import
Каждая находка имеет уникальный ID для обсуждения в PR. Correctness и Security блокируют merge. Performance и Readability — на усмотрение автора.
Мульти-агентный review
Один LLM пропускает ошибки. Два LLM, работающих независимо, пропускают меньше. Исследование Microsoft «CodeReviewer» (2022) показало, что мульти-агентный подход находит на 15-25% больше дефектов, чем одиночный проход.
Практическая реализация: первый агент (Claude) проходит все четыре этапа. Второй агент (GPT или Gemini) проверяет только Correctness и Security. Находки сравниваются. Если оба агента указали на одну проблему, уверенность высокая. Если только один — нужна ручная проверка.
Claude Concilium реализует этот подход через MCP: параллельные запросы к нескольким LLM с объединением результатов.
Интеграция в CI/CD pipeline
Автоматический review при каждом PR. Базовая архитектура:
# .github/workflows/ai-review.yml
name: AI Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
ai-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get changed files
id: diff
run: |
echo "files=$(git diff --name-only origin/main...HEAD | grep -E '\.(ts|py|go|rs)$' | tr '\n' ' ')" >> $GITHUB_OUTPUT
- name: Run AI Review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
for file in ${{ steps.diff.outputs.files }}; do
diff=$(git diff origin/main...HEAD -- "$file")
# Промпт с 4 этапами review
claude --print "Review this diff: $diff" >> review_output.md
done
- name: Post review comments
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const review = fs.readFileSync('review_output.md', 'utf8');
await github.rest.pulls.createReview({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
body: review,
event: 'COMMENT'
});
Ключевые решения при настройке
Что ревьюить. Только изменённые файлы. Полный repo-scan при каждом PR слишком дорог. Фильтруйте по расширениям: исключите .md, .json, конфиги.
Как управлять стоимостью. Один diff на один вызов API. При 10 файлах в PR это 10 вызовов по ~2000 токенов. При claude-sonnet-4 — примерно $0.06 за средний PR. Один час работы ревьюера стоит $50-100.
Блокировать ли merge. На старте — нет. AI review как информационный комментарий. После калибровки (2-4 недели) можно включить блокировку для Correctness и Security с порогом severity >= High.
Ложные срабатывания. Будут. Типичная точность AI code review — 70-80%. Из 10 замечаний 2-3 нерелевантны. Это приемлемо, если review экономит время на остальных 7-8. Для снижения false positives добавляйте проектный контекст в промпт: conventions, типовые паттерны, архитектурные решения.
Метрики эффективности AI review
| Метрика | Как считать | Целевое значение |
|---|---|---|
| True Positive Rate | Принятые замечания / Все замечания | > 70% |
| Blocked Bugs | Баги, найденные AI до merge | Растёт со временем |
| Time to Review | Время от PR до первого комментария | < 5 минут |
| Cost per PR | Стоимость API-вызовов на один PR | < $0.10 |
| Human Review Time | Время ручного ревью после AI | Снижается на 30-50% |
Собирайте данные с первого дня. Через месяц будет ясно, какие категории дают больше находок и какие промпты нужно доработать.
С чего начать
Неделя 1. Возьмите промпт из раздела Correctness. Вручную прогоните через Claude или GPT последние 5 PR вашей команды. Запишите, сколько находок совпало с тем, что поймал (или пропустил) человеческий ревьюер.
Неделя 2. Добавьте промпт Security. Сравните находки с результатами SAST-инструментов (Semgrep, CodeQL). LLM часто ловит логические уязвимости, которые SAST пропускает, но пропускает паттерновые, которые SAST находит стабильно. Они дополняют друг друга.
Неделя 3. Настройте CI pipeline. Начните с одного репозитория, одного языка. Комментарий в PR без блокировки.
Неделя 4. Посчитайте метрики. True Positive Rate ниже 50%? Доработайте промпты, добавьте контекст проекта. Выше 70%? Включайте блокировку для Critical/High.
Четыре категории в фиксированном порядке. Отдельный проход для каждой. Промпт, который запрещает комментировать не свою категорию. Этого достаточно, чтобы поймать большинство дефектов до merge.