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

Запреты, которые работают: перечисление вместо принципа
«Чини по-настоящему» бесполезно: агент и так считает, что чинит по-настоящему.

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

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

Чтение
5 мин
Технологии / версии
Промпты · Coding agent · PHP · Тесты
Серия
Цикл «Промпты» · часть 5 из 6
9 июл 2026 · 5 мин · 5 просмотров · AI
Список запрещённых действий рядом с зелёным индикатором сборки
Промпты 07/2026

Я долго писал в задачах «исправляй по-настоящему, не прячь симптомы». Формулировка мне нравилась: коротко, понятно, взрослому человеку достаточно.

Она не работает вообще никак. И причина не в том, что её игнорируют, а в том, что ей уже следуют. Тот, кто добавляет игнорирование в конфигурацию тестов, не считает, что прячет симптом. Он считает, что убирает шум, мешающий увидеть настоящие проблемы. С его точки зрения это и есть «по-настоящему».

Принцип не отсекает вариантов

Ограничение работает, когда сужает пространство решений. «Чини по-настоящему» его не сужает: любое решение можно описать как настоящее, и оно будет описано именно так.

Проверить легко. Возьмите свою формулировку и спросите: какое конкретное действие она запрещает? Если ответ — «ну, плохие действия», ограничения нет, есть пожелание.

То же с «пиши качественный код», «учитывай безопасность», «следуй лучшим практикам». Все три занимают место в задаче, ни одно ничего не запрещает.

Шесть способов сделать зелёным

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

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

Все шесть правок дают чистый вывод. Все шесть оставляют код, который сломается на следующей мажорной версии, причём молча.

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

Ни один из этих способов не является обманом. Каждый из них в правильной ситуации — нормальная инженерная практика. Проблема не в приёме, а в том, что приём применяется вместо разбора причины, и снаружи разница не видна: сборка зелёная в обоих случаях.

Запрет — это список

Работающая формулировка выглядит скучно. Это перечисление конкретных действий.

ЗАПРЕЩЕНО:
- менять тест, если проблема в коде;
- добавлять пропуск теста, игнорирование по шаблону, подавление ошибки;
- дописывать в базовую линию статического анализа;
- ставить безопасный вызов и оператор объединения с null для обхода
  анализатора без обоснования, почему значение действительно бывает пустым;
- увеличивать таймауты и добавлять повторы попыток.

Если считаешь, что проблема в тесте или в окружении — обоснуй отдельно
и остановись.

Список из пяти строк убрал все шесть случаев подавления полностью. Не уменьшил — убрал.

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

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

Что запрещать на типичных задачах

Три набора, которые у меня переезжают из задачи в задачу.

Задача на тесты. Не менять код приложения ради зелёного теста. Не писать тесты на геттеры ради покрытия. Не мокать то, что можно взять настоящим. Последний пункт появился после набора тестов, где замокан был репозиторий, а проверялось, что репозиторий вызван — тест, который не падает никогда.

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

Задача на красную сборку. Список из предыдущего раздела целиком, плюс «не переходить к следующему падению, пока не разобран текущий».

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

Запреты, вынесенные из текста задачи

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

У меня туда вынесены отправка в удалённый репозиторий, жёсткий сброс, правка файлов окружения и правка базовой линии статического анализа. Разница принципиальная: запрет в тексте задачи — это просьба, запрет в конфигурации — это отказ инструмента выполнить действие.

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

Правило простое: если запрет повторяется в третьей задаче подряд, ему место не в задаче.

Где запрет помешал

Один случай стоит рассказать, потому что он ломает стройность.

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

Правильное действие — исправить тест: добавить сортировку в ожидание или в запрос. Вместо этого я получил обоснование на два абзаца, почему проблема в тесте, и остановку. Формально всё верно, инструкция сработала как задумано. Но я потерял двадцать минут на переписку ради правки в одну строку.

Вывод я сделал не «убрать запрет», а «добавить выход». Строка «если считаешь, что проблема в тесте — обоснуй и остановись» и есть этот выход. Без неё запрет превращается в тупик, и из тупика ищут обход.

Что осталось

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

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

Серия

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

#prompting #coding-agent #php #testy #ci