Плановый режим и разработка через тесты
Предыдущая статья разобрала паттерн explore → plan → code → commit как архитектурное решение для работы с агентом. Здесь — детальный разбор двух инструментов, которые делают этот паттерн надёжным: план-режима и TDD-петли. Оба решают одну проблему: агент сам не знает, когда работа сделана правильно, если вы не дали ему способ это проверить.
Плановый режим: защита от преждевременного кода
Плановый режим (plan mode) — это режим разрешений, в котором Claude может читать файлы, выполнять read-only команды и предлагать изменения, но не может ничего записать на диск. Никаких Edit, никаких Write, никаких деструктивных Bash-команд.
Включить его можно тремя способами:
# Запуск сразу в план-режиме
claude --permission-mode plan
# Переключение в середине текущей сессии
# Shift+Tab — циклически переключает режимы разрешений
# Или явно из командной строки внутри сессии:
/planПереключение через Shift+Tab работает как тоггл: default → acceptEdits → plan → default. Полезно, когда уже находишься в сессии и хочешь безопасно исследовать, прежде чем дать команду на изменения.
Что конкретно происходит в план-режиме:
✅ Разрешено: ❌ Заблокировано:
──────────────────────────────────────────────
Read (чтение файлов) Edit (правка файлов)
Grep, Glob Write (создание файлов)
Bash (read-only) Bash (с побочными эффектами)
Анализ и планирование Все деструктивные командыПосле того как Claude составил план, нажмите Ctrl+G — план откроется в вашем редакторе. Можно убрать ненужные шаги, добавить ограничения, исправить приоритеты. Claude будет следовать именно этой версии плана при выходе из план-режима.
Что делает хороший план
Агент в план-режиме — это архитектор без молотка. Используйте это: задавайте вопросы, которые требуют понимания структуры, а не реализации.
# Слабый запрос для план-режима:
добавь Google OAuth
# Сильный запрос:
мне нужно добавить Google OAuth. Прочитай @src/auth/ и разберись,
как сейчас устроен login-flow. Затем:
1. Покажи, какие файлы нужно изменить и почему
2. Какие новые файлы появятся
3. Как изменится flow сессии после OAuth callback
4. Есть ли риски обратной совместимости с текущими сессиями
Составь план с конкретными именами файлов и шагами.Чем конкретнее вопросы — тем точнее план. Хороший план содержит:
- Файлы с конкретными изменениями (
src/auth/handlers.ts— добавить функциюhandleOAuthCallback) - Последовательность шагов с обоснованием порядка
- Точки верификации («после шага 3 запустить
npm test auth») - Явные ограничения («не менять существующий
sessionMiddleware»)
Есть и практический трюк для сложных фич: дать Claude провести «интервью» перед планом.
хочу реализовать систему уведомлений. Проведи детальное интервью
с помощью AskUserQuestion. Спроси про технические подходы,
UX, крайние случаи и trade-offs — особенно те, о которых я
мог не подумать. После интервью напиши полный spec в SPEC.md.Получив SPEC.md, начните чистую сессию для реализации — контекст планирования не смешивается с контекстом имплементации.
TDD-петля: тест как оракул для агента
Теперь самое важное. Документация Anthropic формулирует это так:
> Give Claude a check it can run: tests, a build, a screenshot to compare. It's the difference between a session you watch and one you walk away from.
Без проверки Claude останавливается, когда «выглядит готово». С тестом он останавливается, когда тест зелёный. Это принципиальная разница.
ТДД в контексте Claude — не обязательно строгий Red-Green-Refactor из книжки. Это паттерн: сначала определить, как выглядит «правильно», а потом дать агенту итерировать до этого состояния.
flowchart TD
A(["Задача: написать функцию X"]) --> B["📝 Написать failing тест\n(в план-режиме или вручную)"]
B --> C["▶ Запустить тест\n→ FAIL"]
C --> D["🤖 Claude: реализовать X\n(в default-режиме)"]
D --> E["▶ Запустить тест"]
E --> F{"Тест\nпрошёл?"}
F -->|"❌ FAIL"| G["Claude: прочитать ошибку\nи исправить"]
G --> E
F -->|"✅ PASS"| H["▶ Запустить полный suite"]
H --> I{"Всё зелёное?"}
I -->|"❌ Регрессия"| G
I -->|"✅ OK"| J(["Готово: коммит и PR"])
style A fill:#e8f4f8,stroke:#2980b9
style J fill:#e8f8e8,stroke:#27ae60
style F fill:#fff3cd,stroke:#f39c12
style I fill:#fff3cd,stroke:#f39c12Практически это выглядит так:
# Шаг 1: напишите falling test (или попросите Claude написать его)
# Шаг 2: дайте Claude задачу с явной точкой остановки
claude --permission-mode plan
> Прочитай src/utils/tokenRefresh.ts и напиши failing тест,
который проверяет: если refresh-токен истёк, функция должна
выбросить TokenExpiredError, а не вернуть null.
Не реализуй ничего — только тест.После того как тест написан и запущен — вы видите красный результат:
FAIL src/utils/tokenRefresh.test.ts
✕ throws TokenExpiredError when refresh token is expired
Expected: TokenExpiredError
Received: nullТеперь выходите из план-режима и даёте Claude полную задачу:
# Выход из план-режима: Shift+Tab
реализуй функцию refreshToken в src/utils/tokenRefresh.ts так,
чтобы она проходила написанный тест. После каждого изменения
запускай `npm test -- tokenRefresh` и итерируй до зелёного.
Если тест падает три раза подряд с одной ошибкой — остановись
и объясни проблему, не продолжай угадывать.Ключевые элементы хорошего TDD-промпта для агента:
1. Конкретная команда проверки (npm test -- tokenRefresh, не просто «запусти тесты»)
2. Итерация до результата («итерируй до зелёного»)
3. Стоп-условие (чтобы агент не уходил в бесконечный цикл при застревании)
Таблица верификаторов
Тест — не единственный способ дать агенту обратную связь. Вот что работает для разных типов задач:
| Тип задачи | Верификатор | Пример команды |
|---|---|---|
| Логика / алгоритмы | Юнит-тест | pytest tests/unit/test_parser.py |
| Рефакторинг | Тест + тайпчек | tsc --noEmit && npm test |
| Баг-фикс | Failing test → fix → pass | Написать тест, воспроизводящий баг |
| UI-изменения | Скриншот + сравнение | claude --chrome + визуальный diff |
| Миграция | Линтер + типы + тесты | eslint . && tsc && pytest |
| API / интеграция | End-to-end | curl + проверка статус-кода |
| Инфраструктура | Dry-run или plan | terraform plan |
Командная строка верификатора должна быть в промпте явно. «Запусти тесты» — слишком неоднозначно: Claude может запустить весь suite вместо одного файла, что медленно и шумно.
Комбинация план-режима и TDD
Оба инструмента работают вместе: план-режим отвечает за понимание и проектирование, TDD — за безопасную итеративную реализацию.
Фаза 1 (план-режим): понять → спроектировать → написать тест
Фаза 2 (default-режим): реализовать → запустить тест → итерировать → зелёныйПолный промпт для сложной задачи:
[В план-режиме]
Изучи @src/payments/ и разберись, как мы обрабатываем
возвраты. Затем:
1. Составь план добавления частичного возврата
2. Напиши failing тест в tests/unit/test_refunds.py,
который проверяет: частичный возврат на сумму больше
оригинального платежа должен выбрасывать OverRefundError
3. Не реализуй ничего — только план и тест[Выход из план-режима: Shift+Tab]
Реализуй план из шага 1. После каждого изменения
запускай `pytest tests/unit/test_refunds.py -v`.
Если все тесты зелёные, запусти полный suite:
`pytest tests/ -x` и убедись, что ничего не сломалось.
Потом открой PR с описательным сообщением.Stop Hook: детерминированный барьер
Для автономных сессий без постоянного надзора есть следующий уровень — Stop Hook. Это shell-команда, которая запускается при каждой попытке агента остановить сессию. Если команда завершается с ненулевым кодом, агент не может завершить работу.
// .claude/settings.json
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "npm test -- --passWithNoTests"
}
]
}
]
}
}С таким хуком Claude не может завершить работу, пока npm test не вернёт 0. Это детерминированный барьер: в отличие от инструкции в CLAUDE.md (которую агент может «забыть»), хук выполняется всегда, независимо от состояния контекста.
Важно: документация указывает, что Claude Code завершает сессию принудительно после 8 последовательных блокировок хуком — защита от бесконечного цикла. Поэтому Stop Hook хорош для задач, где тест реально должен пройти, а не как вечный сторож.
Когда план-режим и TDD не нужны
Оба инструмента добавляют накладные расходы. Не каждая задача их оправдывает.
Пропустите план-режим, если:
- Задача умещается в одно предложение: «добавь null-check в строку 42»
- Вы хорошо знаете изменяемый код
- Diff будет трогать один файл в одном месте
Пропустите TDD, если:
- Задача чисто косметическая (переименование, форматирование)
- Изменение — это документация или конфиг
- Вы сами быстрее проверите результат визуально, чем напишете тест
Правило: если вы не уверены в подходе или не знаете изменяемый код — включайте план-режим. Если результат сложно проверить визуально и есть что тестировать — давайте тест.
See also
- Типовые рабочие процессы: исследование, план, реализация — паттерн explore → plan → code → commit, из которого вырастают оба инструмента этой статьи
- Best practices и паттерны организации кода — полный список рекомендаций Anthropic, включая дисциплину контекста и принцип верификации
- Hooks — события жизненного цикла — детальный разбор Stop Hook и других событий жизненного цикла для детерминированной автоматизации
- Субагенты и контекстная изоляция — паттерн Writer/Reviewer: один агент пишет, другой верифицирует в чистом контексте
- Управление контекстным окном — почему план-режим помогает сохранить контекст чистым и как /clear и /compact дополняют его
- Git, коммиты и пул-реквесты — финальная фаза после TDD-цикла: коммит и PR с осмысленным описанием