almdev Технический блог

Промпты на изменение: план до кода и лимит на дифф
С диффом на 1240 строк невозможно спорить — с ним можно только согласиться.

Ошибка в плане стоит абзаца переписки, та же ошибка в коде — часа ревью и отката миграции. Разбираю, что просить в плане, как ставить потолок размера диффа, зачем останавливаться после каждого шага и где план не спасает.

Подкатегория: Промпты

Чтение
5 мин
Технологии / версии
Symfony · Промпты · Coding agent · PHP
Серия
Цикл «Промпты» · часть 3 из 6
4 июн 2026 · 5 мин · 3 просмотра · AI
План работы из пяти шагов рядом с ограничителем размера изменения
Промпты 06/2026

Дифф на 1240 строк я читал два часа. Нашёл три расхождения с исходным поведением, из них одно серьёзное. Уверен, что нашёл не всё — на восьмисотой строке внимание кончается, а дальше начинается вежливое пролистывание.

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

Где ошибка дешевле

Одна и та же ошибка стоит по-разному в зависимости от того, на каком этапе её заметили.

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

Отсюда основной приём: сначала план, ревью плана, потом код. Не потому что план ценен сам по себе, а потому что на этапе плана моё «нет» стоит абзац, а не час.

Что должно быть в плане

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

Не пиши код. Сначала план.

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

Остановись после плана.

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

На рефакторинге сервиса задач в тот план попала строка «создаю TimeTrackingService, переношу туда четыре метода, интерфейса не ввожу». Я эту строку прочитал и оставил. А вот строку «переношу проверку прав из сервиса в контроллер» вычеркнул сразу, и это стоило мне ровно двух секунд.

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

Потолок диффа как параметр задачи

Второй приём проще первого и работает без плана: назвать максимальный размер изменения.

Дифф не больше 200 строк. Если задача не помещается — останови работу
и скажи, на какие части её делить.

Число здесь не сакральное. Двести строк — это примерно то, что я читаю внимательно за один подход, не теряя нить. У кого-то это триста, у кого-то сто пятьдесят. Важно, что число есть, потому что без него размер определяется задачей, а задача всегда больше, чем кажется.

Побочный эффект оказался ценнее основного. Ограничение в 200 строк заставляет исполнителя выбирать, что делать в первую очередь, а выбор он объявляет вслух. На пятиэтапном рефакторинге первым шагом были предложены характеризующие тесты — не потому что я просил, а потому что переносить методы в пределах лимита без них не получалось.

Второе, что даёт лимит: переговоры вместо факта. Ответ «задача не помещается, предлагаю разбить на пять» приходит до работы, а не после.

Остановка после каждого шага

Формулировка «сделай по шагам» без явной остановки не работает: шаги выполняются подряд, и на выходе всё тот же большой дифф, только с подзаголовками.

Работает явное «остановись»:

Выполни только шаг 1. Прогони тесты. Остановись и покажи дифф.
Я скажу, продолжать ли.

Пять таких проходов дают тот же объём работы, что и один большой, но читаются иначе. В диффе на 200 строк я вижу каждую строку. В диффе на 1240 я вижу структуру и верю, что внутри она такая же, как снаружи.

Замер на одном и том же рефакторинге:

ЗаходФормаМоё время на ревьюПропущенных дефектов
Одним куском1240 строк2 ч3
Пять шагов по 2005 диффов40 мин1

Разница во времени объясняется просто. Большой дифф я читаю дважды: первый раз, чтобы понять замысел, второй — чтобы проверить детали. Маленький — один раз, потому что замысел в нём виден сразу.

Коммиты по смыслу

На автономных прогонах, где я не смотрю на каждый шаг, лимит диффа заменяется требованием к коммитам.

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

Прогон на функции меток дал шесть коммитов на 430 строк: миграция и сущность, привязка к задаче, интерфейс управления, фильтр в списке, отображение на карточке, тесты. Каждый читается отдельно, и это единственная причина, по которой такой PR можно ревьюить.

Требование зелёных тестов на каждом коммите даёт ещё и точки возврата. Пятый шаг оказался неудачным — откат до четвёртого ничего не ломает. Без этого требования откат означает откат всей ветки.

Про сообщения: «реализована функция меток» на шести коммитах подряд — это отсутствие сообщений. Помогает явное «сообщение описывает изменение, а не задачу», иначе название задачи копируется во все шесть.

Где план не спасает

План — не гарантия, а способ удешевить возражение. Три случая, где он не помог.

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

Второй: план формально выполнен, а задача не решена. Пункт «добавить фильтр по метке в список задач» был выполнен фильтром, который не дружил с пагинацией. В плане такого уровня детализации не бывает.

Третий: план длиннее задачи. На правке в тридцать строк ревью плана съедает больше времени, чем ревью самого изменения. Порог у меня примерно на сотне строк ожидаемого диффа: ниже — прошу сразу код.

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

Что осталось

Из трёх приёмов — план, лимит, остановка — самым дешёвым оказался лимит. Одна строка в задаче, работает без подготовки, даёт переговоры вместо факта.

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

Чего до сих пор не научился делать хорошо — ревьюить план быстро. Читаю его так же придирчиво, как код, и трачу минут пятнадцать там, где хватило бы пяти.

Серия

Цикл «Промпты»

#symfony #prompting #coding-agent #php #review