Вернуться к блогу

Разработка на основе спецификаций с Claude Code

Разработка на основе спецификаций с Claude Code

Разработчик, который вводит "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.

Именно эту проблему решает Beadbox.

Видимость в реальном времени того, что делает весь ваш флот агентов.

Попробуйте бесплатно в период бета-тестирования →

Верификация -- это не "компилируется"

Самый частый режим сбоя, который я вижу с Claude Code, -- не то, что агенты пишут плохой код. А то, что никто не проверил вывод по спецификации.

Агент говорит "DONE." Разработчик бегло просматривает diff, находит его разумным и мержит. Через два дня кто-то обнаруживает, что функциональность не обрабатывает граничный случай #3 из критериев приёмки. Теперь вы отлаживаете в проде вместо того, чтобы поймать это на 5-минутном шаге верификации.

Верификация означает механическую проверку каждого критерия приёмки. Не "кажется, работает." Не "тесты проходят" (тесты могут быть неправильными или неполными). Каждый критерий из спецификации получает конкретную проверку.

Вот как выглядит правильный отчёт о завершении:

DONE: Filter bar now persists selected filters across workspace
switches using per-workspace localStorage.

Changes:
- lib/local-storage.ts: Added getWorkspaceFilters/setWorkspaceFilters
  with key format "beadbox:filters:{dbPath}"
- components/filter-bar.tsx: Reads stored filters on mount, writes
  on change. Accepts databasePath prop.
- hooks/use-workspace.ts: Triggers filter restore on workspace switch
  via callback prop.

QA Verification:
1. Open http://localhost:41420, select workspace A
2. Set filters to status=open, type=bug
3. Switch to workspace B via header dropdown
4. Switch back to workspace A
5. Verify filters are still status=open, type=bug
   -> Confirmed: filters restore correctly
6. Close tab, reopen. Filters persist.
   -> Confirmed: localStorage key present, filters applied on mount
7. Run: bd list --status=open --type=bug
   -> Output matches filtered table contents (14 beads)

Acceptance criteria:
- [x] Filters persist across workspace switches (steps 2-5)
- [x] Filters survive browser restart (step 6)
- [x] Filtered view matches bd CLI output (step 7)
- [x] Filters don't bleed between workspaces (step 3: workspace B
      shows defaults)

Unit tests: 3 added (storage read/write/key format). All passing.

Commit: a1b2c3d

Разница между этим и "DONE: Fixed the filter bar" -- это разница между 5-минутной проверкой QA и 30-минутным расследованием. Каждое утверждение в DONE-комментарии подкреплено конкретной проверкой. Каждый критерий приёмки сопоставлен с шагом верификации. Ревьюер точно знает, что было построено, как проверено и куда смотреть, если что-то кажется неправильным.

Beads как контейнер для спецификаций

Жизненный цикл, который я только что описал, нуждается в месте для жизни. Спецификация, комментарий с планом, реализация, отчёт о завершении, результаты верификации. Всё привязано к одной задаче, в одном месте.

Эту проблему решает beads. Beads -- это open-source, локальный трекер задач, спроектированный именно для этого рабочего процесса. Каждый "bead" -- задача с описанием (ваша спецификация), цепочкой комментариев (планы и отчёты о завершении), статусом (open, in_progress, ready_for_qa, closed) и метаданными: приоритет, зависимости, назначения.

Вот как spec-driven жизненный цикл выглядит на практике с CLI bd:

Создайте bead со спецификацией:

bd create --title "Persist filter state across workspace switches" \
  --description "## Problem
The filter bar resets when switching workspaces...

## Acceptance Criteria
1. Select workspace A, set filters...
2. Switch to workspace B..." \
  --type feature --priority p2

Агент берёт работу и публикует план:

bd update bb-a1b2 --claim --actor eng1
bd comments add bb-a1b2 --author eng1 "PLAN: Persist filter state
per-workspace using localStorage.

1. Add workspace-scoped storage helpers...
2. Update filter-bar component...
3. ..."

Агент завершает работу и публикует отчёт:

bd comments add bb-a1b2 --author eng1 "DONE: Filter bar now persists
selected filters across workspace switches.

QA Verification:
1. Open http://localhost:41420...

Acceptance criteria:
- [x] Filters persist across workspace switches
- [x] Filters survive browser restart
...

Commit: a1b2c3d"

bd update bb-a1b2 --status ready_for_qa

QA берёт и верифицирует:

bd show bb-a1b2  # Прочитать спецификацию и DONE-комментарий
# Выполнить шаги верификации
bd comments add bb-a1b2 --author qa1 "QA PASS: All 5 acceptance
criteria verified. Filters persist, restore, and match bd CLI output."

Весь жизненный цикл в bead. Спецификация -- описание. План -- комментарий. Отчёт о завершении -- комментарий. Результат QA -- комментарий. Через шесть месяцев, когда кто-то спросит "как работает сохранение фильтров и почему мы выбрали localStorage вместо URL params?", ответ будет в цепочке комментариев bead.

Когда вы проводите одну спецификацию через этот пайплайн, терминала и bd show достаточно. Но этот рабочий процесс раскрывает свою настоящую ценность, когда вы ведёте несколько спецификаций параллельно.

Масштабирование spec-driven разработки

Представьте реальный сценарий: у вас три агента Claude Code, каждый реализует свою спецификацию. Агент A строит функциональность сохранения фильтров. Агент B добавляет новый API-эндпоинт для статистики workspace. Агент C исправляет баг переподключения WebSocket. Каждый находится на каком-то этапе spec-driven жизненного цикла.

В терминале вам нужно выполнить bd list, чтобы увидеть все активные bead, затем bd show на каждом, чтобы проверить статус и прочитать последний комментарий. Шесть команд для снимка трёх параллельных рабочих потоков. Умножьте на пять или десять агентов, и вы тратите больше времени на проверку статусов, чем на ревью планов.

Здесь вписывается Beadbox. Beadbox -- дашборд реального времени, показывающий состояние каждого bead в вашем workspace. Какие спецификации открыты и ждут агента. У каких опубликованы планы, требующие вашего ревью. Какие в процессе. Какие готовы к верификации QA. Всё обновляется в реальном времени, пока агенты пишут комментарии и меняют статусы через CLI bd.

Beadbox не обязателен для spec-driven разработки. CLI обеспечивает весь жизненный цикл. Но когда вы ведёте несколько spec-driven рабочих процессов параллельно, возможность видеть пайплайн целиком вместо поочерёдного опроса статуса каждого агента меняет скорость, с которой вы можете ревьюить планы, разблокировать агентов и обнаруживать застрявшую работу.

Beadbox бесплатен во время бета-тестирования, а CLI beads, на котором он работает, -- open-source.

Что остаётся верным вне зависимости от инструментов

Используете ли вы beads, GitHub Issues, Linear или обычные текстовые файлы, spec-driven паттерн работает, потому что адресует фундаментальную асимметрию в работе агентов: они быстры в исполнении и слабы в суждениях. Каждая минута, потраченная на написание чёткой спецификации, экономит несколько минут на ревью ошибочного вывода, отладке молчаливых допущений и переделке работы, ушедшей не туда.

Принципы:

  1. Определите "готово" до "начать." Критерии приёмки не опциональны. Это единственное, что делает верификацию возможной.

  2. Планы -- это чекпоинты, а не бюрократия. 30-секундный комментарий с планом экономит 20-минутные переписывания. Ревьюьте план, а не код.

  3. Верификация -- это протокол, а не ощущение. "Мне кажется, нормально" -- не верификация. Сопоставление каждого критерия приёмки с конкретной проверкой -- верификация.

  4. Спецификация -- единственный источник истины. Когда реализация и спецификация расходятся, реализация неправильна. Это правило существует, потому что агенты не поставят под сомнение плохой план. Они добросовестно его выполнят и добросовестно произведут неправильный вывод.

  5. Границы области предотвращают дрифт. Явный список файлов для изменения и раздел "вне области" не дают агенту "улучшать" вещи, которые вы не просили улучшать.

Инвестиция невелика: 10-20 минут на написание спецификации для функциональности, реализация которой занимает час. Отдача велика: стабильные результаты, проверяемый вывод и постоянная запись того, что было построено и почему.

Если вы строите подобные рабочие процессы, поставьте звезду Beadbox на GitHub.

Like what you read?

Beadbox is a real-time dashboard for AI agent coordination. Free during the beta.

Share