# Часть 20. Антипаттерны SDD Антипаттерн — это привычка, которая выглядит как правильный процесс, но ломает результат. В SDD такие ошибки особенно коварны: файлы есть, проверки есть, агент работает быстро, но человек постепенно теряет контроль. Эта часть нужна как диагностическая карта. Если процесс стал тяжёлым, шумным или бесполезным, начните отсюда. ## Спецификация после кода Симптом: агент сначала реализует фичу, а потом пишет `requirements.md`, `plan.md` и `validation.md` под уже готовый код. Почему плохо: спецификация перестаёт направлять работу и превращается в отчёт. В таком режиме она не защищает от лишних решений агента. Как исправить: - перед реализацией коммитить хотя бы черновую спецификацию; - запрещать агенту писать продуктовый код в сессии создания спецификации; - в запросе на слияние явно показывать коммит спецификации до коммита реализации. ## Гигантский `requirements.md` Симптом: один файл требований содержит десятки пунктов, несколько сценариев, будущие фазы и спорные решения. Почему плохо: агент начинает выбирать, что считать главным. Человек тоже перестаёт видеть границы. Как исправить: - дробить фичу на фазы; - выносить будущие идеи в `roadmap.md`; - оставлять в `requirements.md` только то, что нужно для текущей ветки; - спорные решения помечать как вопросы, а не как требования. ## `validation.md`, который никто не запускал Симптом: в проверке есть красивые факты, но нет следов их выполнения. Почему плохо: такой файл создаёт ложное чувство готовности. Он выглядит строго, но не допускает ветку к слиянию. Как исправить: - рядом с каждым обязательным фактом хранить команду или ручной сценарий; - в отчёте агента просить список пройденных, проваленных и непроверенных фактов; - не считать факт подтверждённым, если его нельзя воспроизвести. ## Ослабление фактов после ошибки Симптом: тест упал, после чего агент меняет ожидаемый результат в `validation.md`, а не код. Почему плохо: процесс начинает защищать реализацию агента, а не намерение продукта. Как исправить: - просить агента сначала показать расхождение без правок; - правки `validation.md` ревьюить особенно внимательно; - сохранять причину изменения факта в спецификации или запросе на слияние; - запрещать удаление обязательных фактов без решения человека. ## Ритуальный `/clear` Симптом: `/clear` вызывается между фазами, но после него агент всё равно получает длинное объяснение из чата. Почему плохо: команда создаёт видимость проверки переносимости. На самом деле процесс по-прежнему зависит от памяти человека. Как исправить: - после `/clear` давать агенту только ссылки на файлы; - проверять, может ли новая сессия понять следующую задачу из репозитория; - если не может, дописывать спецификации, а не расширять промпт. ## Навык как магическая кнопка Симптом: команда зовёт навык Qwen Code, но никто не читает `SKILL.md` и не понимает, какие решения он принимает. Почему плохо: навык становится скрытым процессом. При сбое люди не знают, что исправлять. Как исправить: - хранить проектные навыки в репозитории; - ревьюить `SKILL.md` как процессный код; - писать в навыке не только команды, но и ограничения; - не создавать навык до 2–3 повторений ручного процесса. ## `QWEN.md` как свалка Симптом: в `QWEN.md` складывают продуктовые требования, стек, личные предпочтения, временные задачи и заметки по ошибкам. Почему плохо: агент перестаёт отличать постоянные правила от временного контекста. Как исправить: - продуктовые решения хранить в `specs/`; - правила поведения агента хранить в `QWEN.md`; - временные выводы переносить в память или ретроспективу; - регулярно удалять устаревшие правила. ## Хук, который молча меняет проект Симптом: хук форматирует, переписывает или удаляет файлы без явного шага в плане. Почему плохо: часть изменений появляется вне контроля агента и человека. Потом трудно понять, кто изменил поведение. Как исправить: - хуки по умолчанию должны проверять или логировать, а не менять файлы; - автоматическое форматирование разрешать только как явное командное правило; - все изменения файлов должны попадать в обычный `git diff`; - при блокировке хук должен объяснять причину. ## Память как скрытый источник истины Симптом: агент принимает решения на основе памяти, но этих решений нет в `specs/`, `QWEN.md` или `AGENTS.md`. Почему плохо: новый участник команды не увидит основание решения. Другой агент тоже может его не получить. Как исправить: - считать память подсказкой, а не правилом; - переносить повторяющиеся выводы в ревьюируемые файлы; - удалять устаревшую память; - в споре между памятью и спецификацией выбирать спецификацию. ## MCP без задачи Симптом: к проекту подключают несколько MCP-серверов «на будущее». Почему плохо: агент получает лишние полномочия и больше способов ошибиться. Команда перестаёт понимать, какие внешние действия вообще возможны. Как исправить: - подключать MCP только под конкретный сценарий; - ограничивать список инструментов; - хранить конфигурацию в ревьюируемом месте; - отключать экспериментальные серверы после проверки. ## Слишком большой MVP Симптом: первая рабочая версия включает авторизацию, роли, аналитику, красивый интерфейс, миграции, импорт данных и интеграции. Почему плохо: агент быстро создаёт много файлов, но человек не успевает понять качество решений. Как исправить: - первая фаза должна доказывать один риск; - ограничивать работу временем; - возвращаться к последнему зелёному состоянию, если ветка расползлась; - добавлять функции только после фактов, которые уже можно проверить. ## Галлюцинации в коде агента Симптом: агент уверенно ссылается на функцию, метод или пакет, которых не существует. Импорт указывает на несуществующий модуль; вызов использует API, которое появилось только в следующей мажорной версии; в `package.json` добавляется зависимость, которой нет в реестре или которая называется почти как известная (например, `requests-py` вместо `requests`). Почему плохо: галлюцинированные импорты могут не падать на этапе типов, если агент сам же дописал заглушку. Особенно опасны несуществующие имена пакетов: атакующий может заранее зарегистрировать такое имя в реестре (атака `slopsquatting`), и `npm install` тихо притянет вредоносный код. Как исправить: - держать список разрешённых зависимостей в `tech-stack.md`; - любое добавление зависимости — отдельный шаг ревью, а не часть «реализуй фичу»; - при первой ошибке типов или рантайма сверять версию пакета с `package.json`; - перед `npm install` смотреть на имя пакета глазами; имя, отличающееся от привычного одной буквой, — стоп-сигнал; - если агент ссылается на функцию, которой раньше в коде не было, требовать ссылку: «покажи определение или скажи, где оно должно появиться». ## Иллюзии тестирования Симптом: `npm test` зелёный, но баг остаётся. Тесты есть, но они не проверяют то, что заявлено. Типичные подвиды: - **тавтологический тест**: тест сравнивает результат функции с тем же выражением, что и в функции, — переименование переменной всё сломает, но логика никогда не проверяется; - **тест-зеркало**: тест проверяет, что функция возвращает то, что вернула, без независимого ожидаемого значения; - **обман через снапшот**: первый запуск создаёт снапшот, второй сравнивает с ним же; ошибка фиксируется в снапшоте и считается «правильной»; - **тест, который не падает ни при какой ошибке**: единственное утверждение — что функция не выбросила исключение. Почему плохо: зелёный набор тестов перестаёт быть фактом готовности. Спецификация формально выполнена («покрытие 90%»), но никаких реальных гарантий нет. Как исправить: - в `validation.md` требовать факт-репро: команду, которая до фикса падает, а после — проходит (особенно для багфиксов, см. часть 11); - при ревью читать сами тесты, а не только цифру покрытия; - для критичных мест применять мутационное тестирование (для Vitest есть Stryker): сервис вносит мелкие правки в код и проверяет, замечают ли их тесты. Если ни одна мутация не ловится, тесты ничего не проверяют; - запрещать снапшоты для бизнес-логики; снапшоты допустимы только там, где результат — человекочитаемый рендер. ## Разработчик не понимает свой PR Симптом: автор открывает запрос на слияние, но не может объяснить, почему конкретное решение принято; в ответ на вопрос ревьюера он повторно спрашивает агента и пересылает ответ. Почему плохо: ответственность за код размывается. Через полгода никто в команде не сможет сказать, почему здесь именно такая обработка ошибок и был ли это сознательный выбор. Если автор не понимает свой PR, ревьюер не может опереться на его суждение, а будущий читатель будет блуждать в гипотезах. Как исправить: - ввести правило: автор PR обязан в описании одним абзацем своими словами объяснить, что и зачем сделано (см. шаблон в приложении C); - ревьюер задаёт хотя бы один вопрос, на который ответ есть только у человека («почему здесь чтение из базы вне транзакции?»); если автор не отвечает или отвечает выдержкой из чата, PR возвращается на доработку; - в команде — поощрять паттерн «парный SDD»: один пишет спецификацию, второй ревьюит её до реализации (часть 22, парный зачёт); - для себя — после слияния перечитывать `git diff` и формулировать вслух: «я сделал X, потому что Y». Если объяснить не получается, в следующей фиче запрашивайте у агента не только код, но и пояснение в комментариях к PR. ## Дорогая модель на всё Симптом: команда выбирает самую мощную модель для любых задач «ради качества» — поиск файлов, генерация документации, простая однофайловая правка и сложная архитектура идут через один тяжёлый ярус. Почему плохо: бюджет токенов сгорает на рутине, которую дешёвая модель закрывает не хуже. Когда приходит действительно сложная задача или критическое ревью, экономии уже нет, а очередь успела съесть квоту. «Качество ради качества» оборачивается медленным и дорогим процессом без выигрыша в результате. Как исправить: - маршрутизировать задачи по сложности, а не по престижу модели: рутину — на лёгкий ярус, реализацию и ревью — на средний, архитектуру и безопасность — на тяжёлый; - держать средний ярус моделью по умолчанию, а тяжёлый включать по явному правилу (первая попытка провалилась, задача затрагивает много файлов, архитектурное или критичное для безопасности решение); - проводить границу яруса по восстановимости решения, а не по его «важности на словах»; - замерять расход: если львиная доля токенов уходит на triage и поиск, ярус выбран неверно. ## Перегруз инструментами Симптом: к сессии подключены все доступные MCP-серверы и инструменты «на всякий случай». Реальный контекст до сжатия оказывается вдвое-втрое меньше номинального, агент путается в выборе инструмента и чаще ошибается. Почему плохо: каждый включённый инструмент занимает место в контексте и добавляет агенту способов ошибиться. Большой набор инструментов деградирует качество так же, как мусор в `QWEN.md`: модель тратит внимание на ненужное и хуже удерживает задачу. Как исправить: - держать в конфигурации сколько угодно серверов, но включать только нужные под текущий сценарий; - ориентир — меньше десяти включённых интеграций и меньше восьмидесяти активных инструментов одновременно; - если функцию закрывает обычный CLI, оформлять её навыком или командой, а не держать постоянно включённый MCP; - отключать экспериментальные и разовые серверы сразу после проверки. ## Мегафайлы Симптом: бизнес-логика лежит в файлах на тысячи строк. Любая правка требует прочитать и удержать в контексте весь файл целиком. Почему плохо: агент не помещает мегафайл в рабочий контекст и работает по обрывкам. Растёт доля ошибок «с первого раза», правка в одном месте незаметно ломает другое, ревью усложняется. Это та же проблема, что и гигантский `requirements.md`, но на уровне кода. Как исправить: - держать основные файлы в пределах сотен строк, а не тысяч; - дробить модули по ответственности, чтобы задача укладывалась в один-два файла; - при росте файла выносить части в отдельные модули до того, как он перестанет читаться целиком; - модульность здесь — не эстетика, а условие того, что задача делается правильно с первой попытки. ## Сплошной grep Симптом: чтобы найти место для правки, агент гоняет текстовый поиск по всей базе — широкие шаблоны, перебор каталогов, повторные проходы. Почему плохо: каждый широкий проход возвращает много лишнего и съедает токены до того, как агент дошёл до сути. Поиск «по всему» вместо целевого превращается в дорогой способ навигации и заодно засоряет контекст нерелевантными совпадениями. Как исправить: - сужать поиск до конкретного каталога или типа файлов, а не запускать его по корню репозитория; - использовать структуру проекта и ссылки на файлы (`@path`) вместо слепого перебора; - где доступен семантический поиск по коду, предпочитать его текстовому grep — он находит нужное за меньшее число проходов и токенов; - если для одной задачи понадобилось несколько широких проходов поиска, это сигнал улучшить навигацию (карту кода, модульность), а не повторять перебор. ## Диагностический чек-лист Если SDD перестал помогать, ответьте: 1. Есть ли фичи, где спецификация написана после кода? 2. Можно ли выполнить факты из `validation.md` без истории чата? 3. Понятно ли, какие правила живут в `QWEN.md`, а какие в `specs/`? 4. Есть ли хуки, которые меняют файлы без явного шага? 5. Есть ли MCP-серверы без текущей задачи? 6. Есть ли решения, которые живут только в памяти агента? 7. Может ли новый агент после `/clear` продолжить работу по файлам? 8. Можно ли объяснить текущую фазу проекта за одну минуту? Если на три вопроса ответ отрицательный, не добавляйте новые инструменты. Упростите процесс. ## Связанные части - Часть 16 — те же ошибки с позиции ревьюера: как поймать антипаттерн в чужом pull-запросе. - Часть 18 — антипаттерны, которые одновременно являются угрозами безопасности (секреты в спецификациях, MCP без ревью, ослабление `validation.md`). - Часть 9 — матрица «тип фичи → уровень фактов», которая прямо лечит антипаттерн «слабый `validation.md`».