От теории — к файлам
Мы разобрались, что SDD адресован агенту, а тесты рождаются из спеки как артефакт. Теперь посмотрим изнутри: как выглядит реальная рабочая спека и из чего она состоит — на примере того, как это устроено в Claude Code.
Спека внутри Claude Code
У самого Claude Code нет жёсткой фазы «spec»: агент с радостью начнёт писать код по первому же запросу, если его не остановить. Поэтому SDD здесь — не встроенный режим, а дисциплина, которую вы задаёте сами. Способов два, и они дополняют друг друга.
Через CLAUDE.md и собственные команды. Вы заранее кладёте в репозиторий правило: «прежде чем менять код фичи, опиши требования, дизайн и план задач в отдельных Markdown-файлах и дождись подтверждения». Claude Code читает CLAUDE.md в начале сессии и держит это правило в контексте. Свои слэш-команды (например, /spec) оформляют такой флоу в один шаг — агент порождает файлы спеки прямо в репозитории, а не сразу бросается генерировать.
Через Spec Kit. Это набор слэш-команд (/specify, /plan, /tasks, /implement) от GitHub, и Claude Code — один из поддерживаемых агентов: команды работают прямо в его чате. Spec Kit не отдельный продукт, а слой поверх агента, который навязывает порядок «сначала спека, потом код» и складывает результат в Markdown-файлы. Репозиторий: github.com/github/spec-kit.
Идея под обоими подходами одна: агент без контекста пишет «что-то работающее», а не «то, что нужно». Спека — это не бюрократия, а способ передать Claude Code намерение до того, как он начал генерировать. Разница лишь в том, кто следит за порядком: при ручной настройке через CLAUDE.md дисциплину держите вы, а Spec Kit берёт это на себя и не даёт проскочить шаг.
Spec Kit даёт самую чёткую структуру — несколько Markdown-файлов на фичу, по одному на смысловой слой. Та же логика повторяется и в ручном флоу через свои команды Claude Code. Разберём каждый из этих файлов.
flowchart TB
subgraph spec["Спека (Spec Kit)"]
direction TB
R["/specify → spec.md\nUser stories · EARS\nAcceptance criteria"]
D["/plan → plan.md\nАрхитектура\nКлючевые решения"]
T["/tasks → tasks.md\nЗадачи · Refs"]
R --> D --> T
end
T -->|/implement| A["AI-агент\n(Claude Code)"]
R -.->|контекст| A
D -.->|контекст| Arequirements.md: что нужно построить
Это сердце спеки. Здесь — user stories и acceptance criteria, записанные в нотации EARS.
EARS (Easy Approach to Requirements Syntax) — нотация, придуманная Alistair Mavin с коллегами в Rolls-Royce в 2009 году для требований к системе управления авиадвигателем. Смысл простой: единый словарь ключевых слов убирает неоднозначность.
Базовый шаблон:
WHEN [условие или событие] THE SYSTEM SHALL [ожидаемое поведение]Пять структурных шаблонов:
| Шаблон | Ключевые слова | Когда использовать |
|---|---|---|
| Повсеместное | THE SYSTEM SHALL | Поведение активно всегда, без условий |
| Событийное | WHEN … THE SYSTEM SHALL | Реакция на конкретное событие |
| Нежелательное | IF … THEN THE SYSTEM SHALL | Реакция на нежелательное или граничное состояние |
| По состоянию | WHILE … THE SYSTEM SHALL | Поведение во время определённого состояния |
| По запросу | WHERE … THE SYSTEM SHALL | Поведение при включённой фиче или флаге |
Вот фрагмент requirements.md для фичи регистрации:
## FR-01 Регистрация через email
### User story
Как новый пользователь, я хочу создать аккаунт через email и пароль,
чтобы получить доступ к своему профилю.
### Requirements
1. WHEN пользователь отправляет форму с уникальным email и паролем не менее 8 символов
THE SYSTEM SHALL создать учётную запись и поставить письмо в очередь отправки.
2. WHEN пользователь отправляет форму с уже существующим email
THE SYSTEM SHALL вернуть ошибку 409 с сообщением «Email уже зарегистрирован».
3. IF пароль содержит менее 8 символов
THEN THE SYSTEM SHALL отклонить запрос с кодом 422 до создания записи.
### Acceptance criteria
- [ ] Дублирующийся email → 409, учётная запись не создаётся
- [ ] Пароль < 8 символов → 422, учётная запись не создаётся
- [ ] Письмо с подтверждением отправляется в течение 30 секунд
- [ ] Пароль хранится как bcrypt-хэш, plain text недопустимКаждый пункт acceptance criteria — будущий тест. Агент не угадывает edge-кейсы: он берёт каждый критерий и пишет конкретную проверку. Именно так из спеки «рождаются» TDD-тесты, о которых мы говорили в прошлом уроке.
design.md: как будем строить
Второй файл — архитектурный слой. Компоненты, взаимодействие между ними, принятые решения.
## Архитектура регистрации
### Компоненты
- `AuthController` — принимает HTTP-запросы
- `UserService` — бизнес-логика, валидация
- `EmailQueue` — асинхронная очередь (Redis) для отправки писем
### Ключевые решения
- Пароль хэшируется через bcrypt (cost=12); argon2 не выбран
из-за слабой поддержки в текущем стеке
- Email отправляется асинхронно: регистрация не зависит от внешнего SMTPsequenceDiagram
participant C as AuthController
participant S as UserService
participant DB as Database
participant Q as EmailQueue
C->>S: createUser(email, password)
S->>DB: проверить уникальность email
alt Email уже зарегистрирован
DB-->>S: дубликат
S-->>C: 409
else Email свободен
S->>S: bcrypt(password, cost=12)
S->>DB: INSERT user
S->>Q: publish(user.registered)
S-->>C: 201, userId
enddesign.md — эволюция обычного design doc. Разница в адресате: обычный design doc пишут для людей, design.md — для агента. Он должен быть конкретным настолько, чтобы агент принял правильные архитектурные решения, но не скатываться в псевдокод — иначе это уже не «что», а «как».
tasks.md: что делаем и в каком порядке
Третий файл — список задач с трассировкой. Каждая задача ссылается на требование, из которого она выросла.
## Tasks
- [ ] 1. Создать модель User в БД
Поля: id, email (unique), password_hash, created_at, confirmed_at
Refs: FR-01, req 1
- [ ] 2. Реализовать UserService.createUser()
— Проверка уникальности email → 409 при дубликате
— Валидация длины пароля → 422 при нарушении
— bcrypt-хэширование (cost=12)
Refs: FR-01, req 1–3
- [ ] 3. Реализовать AuthController.register()
Парсинг тела запроса, вызов UserService, проброс ошибок
Refs: FR-01
- [ ] 4. Настроить EmailQueue
Публикация события после сохранения пользователя; consumer отправляет письмо
Refs: FR-01, AC: «30 секунд»
- [ ] 5. Написать тесты
test_duplicate_email_returns_409
test_short_password_returns_422
test_email_sent_within_30s
Refs: FR-01, все ACПоле Refs — это трассировка. Если завтра продакт скажет «минимальная длина пароля теперь 12 символов» — сразу видно: нужно обновить req 3 в requirements.md, задачу 2 в tasks.md и тест test_short_password_returns_422. Без трассировки что-то обязательно потеряется.
Spec Kit: те же слои, другой синтаксис
В Spec Kit нет жёстких имён файлов — те же смысловые слои создаются командами:
/specify→ acceptance criteria, user story (≈requirements.md)/plan→ архитектурный план, какие файлы затронуть (≈design.md)/tasks→ пронумерованный список задач с привязками (≈tasks.md)/implement→ агент начинает реализацию по задачам
Каждая команда порождает Markdown-артефакт, который кормит следующую фазу. Именно это и есть «refined context»: агент работает не с сырым промптом, а со структурированным описанием.
Спека, которая помещается в голову
Хорошая спека обладает одним свойством: её можно охватить взглядом в ходе реализации. Как только это перестаёт работать — спека стала частью проблемы.
На практике:
requirements.md— 1–2 страницы на фичу, не двадцатьdesign.md— ключевые решения и диаграмма потока, не перечень всех классовtasks.md— 5–15 задач, каждая реализуема за несколько часов
Если задача описана на полстраницы — её нужно разбить. Если требование звучит как псевдокод — значит, спека скатилась в описание реализации, а не поведения. Именно эти анти-паттерны разберём в следующем уроке.