От теории — к файлам

Мы разобрались, что 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 -.->|контекст| A
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 -.->|контекст| A
Флоу Spec Kit: команды /specify, /plan, /tasks порождают Markdown-файлы спеки, а /implement передаёт задачи и контекст агенту.
Quick recall
Что содержит tasks.md в трёхслойной структуре спеки?

requirements.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 недопустим
Check yourself
Посмотрите на требование №3 в примере выше: «IF пароль содержит менее 8 символов THEN THE SYSTEM SHALL…». Какой из пяти шаблонов EARS здесь использован — и почему не WHEN?

Каждый пункт acceptance criteria — будущий тест. Агент не угадывает edge-кейсы: он берёт каждый критерий и пишет конкретную проверку. Именно так из спеки «рождаются» TDD-тесты, о которых мы говорили в прошлом уроке.

design.md: как будем строить

Второй файл — архитектурный слой. Компоненты, взаимодействие между ними, принятые решения.

## Архитектура регистрации

### Компоненты
- `AuthController` — принимает HTTP-запросы
- `UserService` — бизнес-логика, валидация
- `EmailQueue` — асинхронная очередь (Redis) для отправки писем

### Ключевые решения
- Пароль хэшируется через bcrypt (cost=12); argon2 не выбран
  из-за слабой поддержки в текущем стеке
- Email отправляется асинхронно: регистрация не зависит от внешнего SMTP
sequenceDiagram 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 end
sequenceDiagram
    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
    end
Sequence diagram из design.md: поток регистрации через три компонента
Check yourself
design.md в Kiro и обычный design doc: оба описывают архитектуру. В чём принципиальная разница — кроме того, что один лежит в репозитории рядом с кодом?

design.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
Check yourself
Продакт сообщает: минимальная длина пароля меняется с 8 до 12 символов. Используя трассировку из примера tasks.md — какие именно места нужно обновить и почему?

Поле 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 задач, каждая реализуема за несколько часов

Если задача описана на полстраницы — её нужно разбить. Если требование звучит как псевдокод — значит, спека скатилась в описание реализации, а не поведения. Именно эти анти-паттерны разберём в следующем уроке.