TDD с Claude Code — как заставить AI писать тесты до кода и перестать ловить баги в продакшене
TDD с Claude Code — как заставить AI писать тесты до кода и перестать ловить баги в продакшене
Claude Code по умолчанию делает ровно то, что делал бы джуниор без присмотра: пишет код, потом — может быть — накидывает пару тестов для галочки. Знакомо? Проблема в том, что AI отлично пишет код, который выглядит рабочим, но ломается на edge cases, которые никто не проверил.
TL;DR: Три способа заставить Claude Code работать по TDD: ручные промпты (бесплатно, ненадёжно), субагенты с изолированным контекстом (надёжно, требует настройки), и плагин tdd-guard (автоматически блокирует код без тестов). По данным F22 Labs, разработчики, использующие TDD с Claude Code, фиксируют 70% меньше багов в продакшене.
Почему Claude Code не делает TDD сам
Вся суть TDD — ты не знаешь реализацию, когда пишешь тест. Тест описывает поведение, а код подстраивается. Но когда Claude Code работает в одном контексте, он видит и тесты, и реализацию одновременно — и начинает подгонять тесты под код, а не наоборот.
Алекс из alexop.dev сформулировал это точно: «LLM не может реально следовать TDD, если все фазы работают в одном контексте — детали реализации загрязняют дизайн тестов».
Три подхода к решению — от простого к надёжному.
Способ 1: Промпты — бесплатно, но дисциплина на тебе
Самый быстрый старт. Добавляешь в CLAUDE.md правила, и Claude Code старается их соблюдать:
# Testing Rules - Always write failing tests BEFORE implementation - Run tests to confirm RED state before writing code - Implementation must ONLY make failing tests pass — no extras - Never modify tests to make them pass — fix the code
А в сессии используешь конкретные промпты вместо расплывчатых:
# Плохо: "Добавь тесты для авторизации" # Хорошо: "Напиши FAILING тест в tests/auth_test.py для случая logout без активной сессии. НЕ пиши реализацию. Используй pytest, без моков."
После того как тест написан и упал — второй промпт:
"Напиши минимальную реализацию, чтобы тест прошёл. НЕ меняй тест. НЕ добавляй фичи сверх того, что тест проверяет."
Проблема: по данным alexop.dev, без автоматизации Claude Code соблюдает TDD-скиллы лишь в ~20% случаев. Он «забывает» правила через 5-6 промптов, особенно в длинных сессиях.
Способ 2: Три субагента — каждый знает только своё
Самый элегантный подход — разделить red-green-refactor на три изолированных субагента. Каждый работает в своём контексте и физически не видит то, что делают остальные.
Агент 1 — tdd-test-writer (фаза RED):
# .claude/agents/tdd-test-writer.md --- description: Writes failing tests for new features tools: Read, Glob, Grep, Write, Edit, Bash --- You write integration tests that MUST FAIL when run. - Read existing code to understand interfaces - Write tests that describe DESIRED behavior, not current - Run tests and confirm failure - Return: test file path, failure output, behavior summary NEVER write implementation code. NEVER stub return values.
Агент 2 — tdd-implementer (фаза GREEN):
# .claude/agents/tdd-implementer.md --- description: Minimal implementation to pass tests tools: Read, Glob, Grep, Write, Edit, Bash --- You write ONLY the code needed to make failing tests pass. - Read test files to understand expected behavior - Write minimal implementation - Run tests and confirm they pass - Return: modified files, test output NEVER modify test files. No extras, no "nice to haves."
Агент 3 — tdd-refactorer (фаза REFACTOR):
# .claude/agents/tdd-refactorer.md --- description: Improves code quality while keeping tests green tools: Read, Glob, Grep, Write, Edit, Bash --- Evaluate: duplication, naming, component thickness, reusability. If code is clean — return "No refactoring needed" with reasoning. After changes — run tests, confirm green.
Оркестрация — через скилл, который вызывает агентов последовательно:
# .claude/skills/tdd/SKILL.md 1. Call tdd-test-writer → get failing test 2. STOP if test doesn't fail — something wrong 3. Call tdd-implementer → get passing code 4. STOP if tests don't pass — fix before proceeding 5. Call tdd-refactorer → optional cleanup 6. Commit with descriptive message
Каждый субагент изолирован: test-writer не видит план реализации, implementer не может поменять тесты. Это и есть настоящий TDD.
Способ 3: tdd-guard — плагин, который блокирует нарушения
tdd-guard — open-source плагин, который использует хуки Claude Code для перехвата изменений файлов. Если ты пытаешься написать код без failing теста — он буквально останавливает Claude и объясняет, что нужно сделать сначала.
Установка за три команды:
/plugin marketplace add nizos/tdd-guard /plugin install tdd-guard@tdd-guard /tdd-guard:setup
Что он блокирует:
- Код без теста — нельзя написать реализацию, пока нет falling теста
- Перереализацию — нельзя писать больше, чем требуют текущие тесты
- Пропуск рефакторинга — lint-интеграция напоминает про cleanup
Поддерживает Vitest, Jest, pytest, PHPUnit, Go test, Rust — так что работает практически для любого стека. Требует Node.js 22+.
Бонус: хук для принудительной активации скиллов
По данным alexop.dev, одна из главных проблем — Claude «забывает» про TDD-скиллы даже когда они настроены. Решение — хук UserPromptSubmit, который перед каждым промптом заставляет Claude оценить, какие скиллы нужны:
{ "hooks": { "UserPromptSubmit": [{ "matcher": "", "hooks": [{ "type": "command", "command": "npx tsx .claude/hooks/user-prompt-skill-eval.ts" }] }] } }
Хук инжектит в каждый промпт инструкцию: «Сначала оцени — нужен ли TDD-скилл? Да/нет и почему». По замерам автора, это подняло активацию скиллов с ~20% до ~84% — проверено на 200+ промптах.
Подводные камни
1. Субагенты дороже одиночной сессии. Три агента = три контекстных окна = примерно 3× расход токенов на фичу. Для Max-плана это терпимо, для Pro — ощутимо. Совет: запускай субагентов на Sonnet через model: sonnet в frontmatter, а оркестратор оставляй на Opus.
2. tdd-guard требует Node.js 22+. Если проект на Python или Go и Node не установлен — придётся ставить только ради плагина. Для чисто Python-проекта проще субагенты.
3. Длинные интеграционные тесты убивают цикл. Если один прогон тестов занимает 30+ секунд, red-green-refactor превращается в red-wait-green-wait-refactor-wait. Выход — запускай только изменённые тесты: pytest tests/test_auth.py::test_logout -x вместо полного pytest.
4. Claude иногда «подглядывает». Даже с субагентами, если test-writer имеет доступ к Read и может прочитать существующую реализацию — изоляция неполная. Для максимальной чистоты ограничивай test-writer'у доступ только к тестовым файлам и интерфейсам.
5. Рефакторинг-агент может «улучшить» рабочий код до нерабочего. Если рефакторер слишком агрессивен, он выносит логику в абстракции, которые ломают следующие тесты. Добавь в его инструкции: «Если сомневаешься — верни 'No refactoring needed'».
Вердикт
Из трёх подходов субагенты дают самый надёжный TDD — физическая изоляция контекста решает главную проблему AI, которая заключается в подгонке тестов под код. tdd-guard — лучший выбор для тех, кто не хочет писать агентов вручную, но готов поставить Node.js 22. Промпты в CLAUDE.md работают лишь как напоминание, а не как enforcement.
Реалистичный эффект: не 70% меньше багов (эта цифра предполагает идеальное следование), а скорее 30-40% — потому что на практике ты будешь пропускать рефакторинг-фазу и иногда «разрешать» Claude дописать чуть больше, чем нужно. Но даже 30% — это разница между «опять пятница с хотфиксом» и «спокойный деплой».
Как попробовать
- Минимальный старт: добавь в CLAUDE.md четыре правила из «Способа 1» — это занимает 30 секунд
- Установи tdd-guard: три команды выше, 2 минуты — и Claude Code физически не сможет пропустить тесты
- Попробуй промпт:
"Напиши FAILING тест для [твоя фича]. НЕ пиши реализацию. Запусти тест и покажи, что он падает." - Для продвинутых: создай три субагента из «Способа 2» — настройка ~2 часа, но потом каждая фича идёт по рельсам
- Документация: Claude Code Best Practices, tdd-guard на GitHub, полный гайд alexop.dev