Menu

Промпты для код-ревью: как проверять код с ИИ

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

Каждый промпт на этой странице можно изменить и открыть в ChatGPT, Claude или другом ИИ-приложении.

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

От «проверь это» к чек-листу с серьёзностью

В коде на обеих вкладках два настоящих бага. Сравните, как их подаёт каждый ответ.

Можешь проверить этот код? def get_user(conn, username): cur = conn.cursor() cur.execute(f"SELECT id, email FROM users WHERE name = '{username}'") row = cur.fetchone() return {"id": row[0], "email": row[1]}
Try it
Example replyReplies vary between models and runs.

В целом функция короткая и понятная. Несколько советов:

  • Добавьте аннотации типов, например username: str, чтобы сигнатура была яснее.
  • Добавьте docstring с описанием того, что возвращает функция.
  • Используйте параметризованные запросы. Сборка SQL через f-строки может привести к SQL-инъекции.
  • Обработайте отсутствующих пользователей. fetchone() может вернуть None.
  • Именование: fetch_user, возможно, лучше описывает обращение к базе данных.

Первый ответ нашёл оба бага, и в этом проблема оценки ревью по тому, что в нём упомянуто. Инъекция стоит третьей в списке из пяти, смягчена до «может привести», без исправления, рядом с советом про docstring. Читатель, пробежав ответ глазами, добавил бы аннотации типов и пошёл дальше.

Второй промпт изменил четыре вещи:

  • Рамки. «Только баги, безопасность и обработка ошибок. Стиль пропускай» убирает шум. Стиль: работа линтера и форматтера, которые быстрее и никогда сами себе не противоречат.
  • Серьёзность. Она заставляет модель ранжировать, а ранжированный список говорит, с чего начать.
  • Вход, который вызывает проблему. «x' OR '1'='1» превращает расплывчатый риск в демонстрацию, которую можно запустить. Это ещё и лучший фильтр ложных срабатываний, как показывает последний раздел.
  • Диффы. Маленький дифф на каждую находку можно применить или отклонить отдельно. Переписанная функция смешивает все исправления с изменениями, о которых никто не просил.

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

Контекст, который нужен ревьюеру

Ревьюер-человек знает, для чего код и откуда приходят его входные данные. Модель знает только то, что вы вставили. «username приходит прямо из формы входа»: это предложение, которое делает SQL-инъекцию находкой высокой серьёзности, а не теоретической. Полезный контекст для ревью:

  • Откуда приходят входные данные: от пользователей, из другого внутреннего сервиса, из файла конфигурации, который вы контролируете.
  • Что вызывает код и что ожидает получить обратно.
  • Среда выполнения: версия языка, фреймворк, драйвер базы данных. Исправление инъекции выше использует ?, потому что такой заполнитель у sqlite3; некоторые другие драйверы используют %s.
  • Что для этого изменения важнее всего: правильность, безопасность, производительность или всё сразу.

Промпт для ревью, который можно переиспользовать

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

Шаблон для код-ревью
Fill in
Parts
Ты старший ревьюер кода на JavaScript. Ты сообщаешь только о реальных проблемах и ни о чём больше.
Проверь код ниже, сосредоточившись на багах и обработке ошибок.
Код выполняется в браузере. `ids`: массив числовых id из нашего собственного API.
async function loadAll(ids) { const results = []; ids.forEach(async (id) => { const res = await fetch(`/api/items/${id}`); results.push(await res.json()); }); return results; }
Для каждой находки: серьёзность (высокая, средняя или низкая), строка, вход или ситуация, которая её вызывает, и минимальный дифф. Самые серьёзные первыми.
Никаких замечаний о стиле, именовании или форматировании. Если не уверен, что находка реальна, так и скажи и скажи, что её подтвердило бы. Если ничего не нашёл, скажи «Находок нет».
Try it
Example replyReplies vary between models and runs.

1. Высокая: функция возвращается раньше, чем завершается хоть один запрос (строка 3) forEach не ждёт асинхронных колбэков. loadAll сразу возвращает пустой массив results, а добавления происходят позже. Любой вызывающий код получает [].

2. Средняя: неудачные запросы считаются данными (строка 5) Ответ 404 или 500 в fetch не является исключением. JSON-тело с ошибкой разбирается и добавляется так, будто это элемент, а тело не в формате JSON заставляет res.json() выбросить исключение внутри колбэка, который никто не ждёт.

Исправление для обеих, которое заодно сохраняет порядок результатов таким же, как в ids:

 async function loadAll(ids) {
-  const results = [];
-  ids.forEach(async (id) => {
-    const res = await fetch(`/api/items/${id}`);
-    results.push(await res.json());
-  });
-  return results;
+  return Promise.all(
+    ids.map(async (id) => {
+      const res = await fetch(`/api/items/${id}`);
+      if (!res.ok) throw new Error(`Item ${id}: HTTP ${res.status}`);
+      return res.json();
+    })
+  );
 }

Строка с ролью задаёт планку («только о реальных проблемах»), и именно здесь ролевой промптинг помогает больше всего. Если ревью читает программа, например скрипт, который публикует находки комментариями к pull request, просите JSON с фиксированными полями вместо прозы; как это сделать, показано на странице структурированный вывод.

Что делать с ложными срабатываниями

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

Проверяйте каждую находку её входом. Если вы не можете подобрать вход, который в вашей системе доходит до проблемы, возразите, дав недостающий контекст, а не принимайте исправление:

Оспорить находку
Ты отметил эту функцию, потому что `cents` может оказаться undefined или строкой: function formatPrice(cents) { return `$${(cents / 100).toFixed(2)}`; } Её вызывает только `renderCart`, который выходит досрочно, если `Number.isInteger(cents)` не true. Это всё ещё проблема? Если нет, отзови находку.
Try it
Example replyReplies vary between models and runs.

С такой проверкой нет: formatPrice всегда получает целое число, так что находка неприменима, и я её отзываю.

Одного проверка не покрывает: отрицательные целые проходят Number.isInteger, и formatPrice(-500) возвращает "$-5.00". Если до этой функции могут доходить возвраты или скидки, возможно, вам нужно "-$5.00". Если не могут, менять нечего.

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

Часто задаваемые вопросы

Какой промпт хорош для код-ревью?

Назовите, что проверять (баги, безопасность, обработку ошибок), попросите указать серьёзность каждой находки и требуйте конкретный вход, который вызывает проблему, плюс дифф, который её исправляет. Скажите модели пропускать форматирование и именование, если об этом не просили. Дайте ей контекст, который был бы у ревьюера-человека: для чего этот код и что его вызывает.

Может ли ИИ заменить код-ревью человеком?

Нет, но это полезный первый проход. Он быстрый и хорошо ловит частые паттерны багов: необработанный None, пропущенные await, SQL, собранный из строк. Он не знает правил вашего продукта, остальной кодовой базы и причин, по которым было принято решение, и часть его находок окажется неверной. Используйте его перед ревью человеком, а не вместо него.

Почему ИИ в код-ревью сообщает о проблемах, которых нет?

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

Стоит ли просить ИИ переписать код во время ревью?

Просите маленькие диффы, по одному на находку, а не переписанный файл. Полное переписывание смешивает исправления с изменениями, о которых никто не просил, и ревьюить придётся ещё и само переписывание. Диффы позволяют принять или отклонить каждую находку отдельно.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ