Разработчик, который вводит "add user authentication" в Claude Code, каждый раз получает разный результат. Может быть JWT. Может быть сессионные куки. Может быть полный OAuth2-поток с refresh-токенами и PKCE. Агент не знает, чего вы хотите, потому что вы не сказали. Вы указали направление, а не пункт назначения.
Разработчики, которых я вижу получающими стабильный, готовый к релизу вывод из Claude Code, разделяют одну привычку: они пишут спецификацию, прежде чем передать работу агенту. Не роман. Не тикет в Jira с тремя предложениями контекста. Конкретный документ, определяющий, как выглядит "готово", до того как кто-либо напишет строку кода.
Это не новая мудрость. Spec-first разработка появилась за десятилетия до ИИ. Но с агентами цена пропуска спецификации выше, а выгода от её написания больше. Человек-разработчик может остановиться посреди реализации и спросить: "Подожди, ты имел в виду аутентификацию по паролю или SSO?" Агент молча выберет один вариант и продолжит. Когда вы заметите, он уже построил не то, и вы потратили 20 минут на ревью кода, который нужно выбросить.
Эта статья проходит через spec-driven жизненный цикл, который я использую с Claude Code каждый день: как писать спецификации, по которым агенты могут выполнять работу, чекпоинт plan-before-code, который ловит недоразумения на ранней стадии, и протокол верификации, более строгий, чем "компилируется."
Почему "просто сделай" не работает с агентами
Давайте конкретно разберём режим сбоя. Когда вы даёте Claude Code расплывчатую инструкцию, три вещи идут не так:
Молчаливые допущения. Агент заполняет каждый пробел в вашей спецификации собственными допущениями. Иногда они разумны. Иногда нет. Вы не узнаете, в какой категории находитесь, пока не прочитаете вывод. С расплывчатыми инструкциями вы читаете вывод внимательнее, чем потратили бы на написание спецификации.
Невоспроизводимые результаты. Запустите один и тот же расплывчатый промпт дважды и получите две разные реализации. Не просто разные имена переменных или форматирование. Разные архитектурные решения. Разные библиотеки. Разные стратегии обработки ошибок. Если вы не можете воспроизвести вывод, вы не можете построить надёжный процесс вокруг него.
Ревью становится узким местом. Когда агент принимает все решения, вам нужно проверить все решения. Diff на 400 строк, где вы понимаете каждый выбор, занимает 5 минут на ревью. Diff на 400 строк, где агент выбрал схему БД, форму API, коды ошибок и логику валидации, занимает 30 минут, потому что вы реконструируете спецификацию из реализации.
Решение не в лучших промптах. Оно в том, чтобы вынести важные решения вперёд, в документ, по которому агент может работать.
Жизненный цикл spec-driven разработки
Рабочий процесс состоит из пяти фаз. У каждой есть чёткое условие входа и чёткое условие выхода.
Фаза 1: Мозговой штурм. Вы исследуете пространство проблемы. Каковы ограничения? Какие подходы существуют? Что вы пробовали раньше? Здесь вы думаете вслух, самостоятельно или с Claude Code в режиме диалога. Условие выхода: у вас есть предпочтительный подход и вы понимаете компромиссы.
Фаза 2: Ревью. Вы проверяете подход под давлением. Что может пойти не так? Какие граничные случаи существуют? Конфликтует ли это с чем-то в кодовой базе? Если вы работаете с несколькими агентами, здесь полезен агент-архитектор или второе мнение. Условие выхода: вы уверены, что подход надёжен.
Фаза 3: Спецификация. Вы записываете принятое решение. Постановка проблемы, предлагаемый подход, файлы для изменения, механически верифицируемые критерии приёмки и план тестирования. Это контракт. Условие выхода: кто-то (человек или агент) может прочитать эту спецификацию и точно знать, что строить и как верифицировать.
Фаза 4: Реализация. Агент работает по спецификации. Не по расплывчатой идее. По конкретному документу с тестируемыми критериями. Условие выхода: агент заявляет о завершении и опубликовал свидетельства верификации.
Фаза 5: Верификация. Вы (или QA-агент) подтверждаете, что реализация соответствует спецификации. Не "выглядит правильно", а "удовлетворяет каждому критерию приёмки." Условие выхода: каждый критерий проверен, и те, что не прошли, возвращаются к Фазе 4.
Ключевой инсайт: фазы 1-3 дёшевы. Они занимают 10-20 минут для функциональности среднего размера. Фаза 4 занимает столько, сколько требует реализация. Фаза 5 занимает 5-10 минут. Пропуск фаз 1-3 не экономит 10-20 минут. Он стоит времени на ревью, отладку и переделку работы, которая пошла не в ту сторону.
Как выглядит хорошая спецификация для агента
Вот реальный шаблон спецификации. Не пользовательская история. Не документ требований к продукту. Рабочий документ, который говорит агенту, что именно строить.
## Problem
The filter bar resets when switching workspaces. Users lose their
filter state and have to re-apply filters every time they switch.
## Approach
Persist filter state per-workspace in localStorage. Key the stored
state by workspace database path so filters don't bleed across
workspaces.
## Files to Modify
- lib/local-storage.ts: Add getWorkspaceFilters / setWorkspaceFilters
- components/filter-bar.tsx: Read initial state from localStorage,
write on every change
- hooks/use-workspace.ts: Trigger filter restore on workspace switch
## Acceptance Criteria
1. Select workspace A, set filters to status=open + type=bug
2. Switch to workspace B. Filters reset to defaults.
3. Switch back to workspace A. Filters restore to status=open + type=bug.
4. Close the browser tab, reopen. Filters for the active workspace
are still applied.
5. bd list --status=open --type=bug output matches the filtered table.
## Out of Scope
- Server-side filter persistence
- Filter presets / saved filter combinations
- URL-based filter state (query params)
## Test Plan
- Unit test: getWorkspaceFilters returns stored value for matching
workspace path
- Unit test: setWorkspaceFilters writes correct key format
- Manual test: steps 1-5 from acceptance criteria above
Обратите внимание, что содержит и не содержит эта спецификация. Она не объясняет, как работает localStorage. Claude Code это знает. Она не обосновывает, почему мы выбрали localStorage вместо URL params. Это произошло на этапе мозгового штурма. Она перечисляет каждый файл, который агент должен затронуть, что означает: если агент начинает изменять файлы за пределами этого списка, это тревожный сигнал. Она включает раздел "вне области", который предотвращает gold plating со стороны агента.
Критерии приёмки -- самая важная часть. Каждый из них -- конкретное действие с наблюдаемым результатом. Не "фильтры должны сохраняться." Это неоднозначно. "Переключиться на workspace A, убедиться, что фильтры -- status=open + type=bug" -- тестируемо. Агент может выполнить это. QA-ревьюер может проверить.
Паттерн plan-before-code
Вот протокол, который ловит большинство ошибок реализации до того, как они станут кодом: требовать от агента опубликовать план реализации, прежде чем писать что-либо.
На практике это выглядит как структурированный комментарий к задаче. Прежде чем агент откроет какой-либо файл, он записывает, что именно собирается делать.
PLAN: Persist filter state per-workspace using localStorage.
1. Add workspace-scoped storage helpers to lib/local-storage.ts:
- getWorkspaceFilters(dbPath: string): FilterState | null
- setWorkspaceFilters(dbPath: string, filters: FilterState): void
- Key format: "beadbox:filters:{dbPath}"
2. Update components/filter-bar.tsx:
- On mount, read stored filters for current workspace
- On every filter change, write to localStorage
- Accept databasePath as prop (currently not passed)
3. Update hooks/use-workspace.ts:
- On workspace switch, emit event that filter-bar listens for
- Filter-bar reads new workspace's stored filters on event
4. Threading databasePath:
- filter-bar needs databasePath from the workspace context
- Will add it to the WorkspaceProvider context value
Files: lib/local-storage.ts, components/filter-bar.tsx,
hooks/use-workspace.ts, components/workspace-provider.tsx
Test: Unit tests for storage helpers, manual test of all 5
acceptance criteria.
Агенту требуется около 30 секунд, чтобы это написать. Вам -- около 2 минут, чтобы прочитать. И за эти 2 минуты вы можете поймать проблемы, на исправление которых после реализации уйдёт 20 минут:
- Затрагивает ли агент файлы вне спецификации? (Добавление workspace-provider.tsx не было в спецификации. Это нормально или scope creep?)
- Имеет ли подход смысл? (Event emitter для переключения workspace может быть overengineering. Более простое изменение пропсов может сработать.)
- Не пропущены ли шаги? (Как насчёт очистки устаревших записей localStorage при удалении workspace?)
План -- это чекпоинт. Если он выглядит правильно, скажите агенту продолжать. Если неправильно, скорректируйте план. В любом случае вы потратили 2 минуты вместо 20.
