Четырехфазный процесс SDD
Spec-Driven Development -- это не просто "напиши спеку и дай AI". Это строгий четырехфазный процесс с явными гейтами между фазами. Пропуск фазы или гейта гарантированно приводит к проблемам -- точно так же, как пропуск проектирования в строительстве приводит к обрушению конструкции.
┌──────────┐ Gate 1 ┌──────────┐ Gate 2 ┌──────────┐ Gate 3 ┌──────────────┐
│ SPECIFY │──────────────│ PLAN │──────────────│ TASKS │──────────────│ IMPLEMENT │
│ │ Review & │ │ Review & │ │ Review & │ │
│ Specs │ Approve │ Steps │ Approve │ Atomic │ Approve │ AI + Verify │
└──────────┘ └──────────┘ └──────────┘ └──────────────┘
↑ │
└──────────────────── Feedback Loop ───────────────────────────────────────────┘
Ключевой принцип: Каждая фаза завершается гейтом -- точкой проверки, после которой следующая фаза может начаться. Гейт -- это не формальность. Это момент, когда вы проверяете: "Достаточно ли информации для следующего шага?"
Эта глава посвящена первым двум фазам: Specify и Plan. Фазы Tasks и Implement рассмотрены в следующей статье.
Фаза 1: SPECIFY -- создание спецификации
Цель фазы
Создать полный документ спецификации, который фиксирует весь замысел функциональности. Спецификация -- это контракт между вами и AI: всё, что не написано в спецификации, AI будет додумывать сам. А додумывание AI -- это рулетка.
Плохо: "Сделай регистрацию пользователей"
Лучше: "Сделай регистрацию с email, паролем, подтверждением"
Хорошо: Полная спецификация на 2-3 страницы (см. ниже)
Шаги создания спецификации
Шаг 1: Определение проблемы
Начните с главного вопроса: какую проблему мы решаем? Не "что мы хотим построить", а "зачем это нужно".
## Problem Statement
Users currently cannot create accounts on the platform.
This blocks all features that require authentication:
personal progress tracking, quiz history, learning paths.
**Impact**: 100% of personalized features are inaccessible.
**Root cause**: No user registration system exists.
**Priority**: Critical — blocks core platform value.
Важно: Если вы не можете четко сформулировать проблему в 2-3 предложениях, вы не готовы к спецификации. Вернитесь к анализу требований.
Шаг 2: Критерии успеха
Как мы узнаем, что задача решена? Критерии должны быть измеримыми и проверяемыми.
## Success Criteria
1. User can register with email and password
2. Email confirmation is sent within 30 seconds
3. Duplicate email registration returns clear error
4. Password meets minimum security requirements (8+ chars, mixed case, digit)
5. Registered user can log in immediately after email confirmation
6. Registration API responds within 200ms (p95)
7. All endpoints have OpenAPI documentation
| Критерий | Плохой пример | Хороший пример |
|---|---|---|
| Производительность | "Должно быть быстро" | "Ответ < 200ms на p95" |
| Безопасность | "Пароль должен быть надежным" | "Минимум 8 символов, upper + lower + digit" |
| UX | "Понятные ошибки" | "Код ошибки + человекочитаемое сообщение на русском" |
| Покрытие | "Тесты написаны" | "Unit tests: 90%+ покрытие, integration: все эндпоинты" |
Шаг 3: Функциональные требования
Что система должна делать. Каждое требование -- отдельный пункт с уникальным идентификатором.
## Functional Requirements
### FR-1: User Registration
- FR-1.1: System accepts email and password for registration
- FR-1.2: System validates email format (RFC 5322)
- FR-1.3: System checks email uniqueness against database
- FR-1.4: System hashes password using bcrypt (cost factor 12)
- FR-1.5: System creates user record with status "pending_confirmation"
- FR-1.6: System sends confirmation email with unique token (valid 24h)
### FR-2: Email Confirmation
- FR-2.1: System accepts confirmation token via GET request
- FR-2.2: System validates token existence and expiration
- FR-2.3: System updates user status to "active" on valid token
- FR-2.4: System invalidates token after use (one-time use)
- FR-2.5: System returns appropriate error for expired/invalid tokens
### FR-3: Login
- FR-3.1: System accepts email and password for authentication
- FR-3.2: System verifies password against stored hash
- FR-3.3: System returns JWT token pair (access + refresh)
- FR-3.4: System rejects login for unconfirmed accounts
Шаг 4: Нефункциональные требования
Что система должна обеспечивать помимо функциональности.
## Non-Functional Requirements
### NFR-1: Performance
- Registration endpoint: < 200ms (p95)
- Email sending: async, within 30 seconds
- Database queries: < 50ms each
### NFR-2: Security
- Passwords: bcrypt, cost factor 12
- Rate limiting: 5 registration attempts per IP per hour
- JWT: RS256 signing, access token 15min, refresh token 7 days
- CSRF protection on all state-changing endpoints
- Input sanitization: no HTML/script injection
### NFR-3: Scalability
- Support 100 concurrent registrations
- Email queue handles 1000 messages/hour
### NFR-4: Observability
- Structured logging for all registration events
- Metrics: registration count, confirmation rate, failure rate
- Alerting: failure rate > 5% triggers notification
Шаг 5: API-контракты
Для каждого эндпоинта -- полное описание запроса и ответа. Это самая важная часть спецификации для AI, потому что API-контракт -- это однозначная, машиночитаемая инструкция.
# OpenAPI 3.1 specification
openapi: 3.1.0
info:
title: User Registration API
version: 1.0.0
paths:
/api/v1/auth/register:
post:
summary: Register a new user
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [email, password, password_confirmation]
properties:
email:
type: string
format: email
example: "[email protected]"
password:
type: string
minLength: 8
example: "SecurePass1"
password_confirmation:
type: string
example: "SecurePass1"
responses:
'201':
description: User registered successfully
content:
application/json:
schema:
type: object
properties:
id:
type: string
format: uuid
email:
type: string
status:
type: string
enum: [pending_confirmation]
message:
type: string
example: "Confirmation email sent"
'400':
description: Validation error
content:
application/json:
schema:
$ref: '#/components/schemas/ValidationError'
'409':
description: Email already registered
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'429':
description: Rate limit exceeded
/api/v1/auth/confirm/{token}:
get:
summary: Confirm email address
parameters:
- name: token
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Email confirmed
'400':
description: Invalid or expired token
'404':
description: Token not found
components:
schemas:
ValidationError:
type: object
properties:
code:
type: integer
example: 400
message:
type: string
example: "Validation failed"
errors:
type: object
additionalProperties:
type: array
items:
type: string
example:
email: ["Invalid email format"]
password: ["Must be at least 8 characters"]
Error:
type: object
properties:
code:
type: integer
message:
type: string
Шаг 6: Модели данных
## Data Models
### User Entity
| Field | Type | Constraints | Description |
|-------|------|-------------|-------------|
| id | UUID v7 | PK, auto-generated | Unique identifier |
| email | VARCHAR(255) | UNIQUE, NOT NULL | User email address |
| password_hash | VARCHAR(255) | NOT NULL | Bcrypt hash |
| status | ENUM | NOT NULL, DEFAULT 'pending' | pending, active, blocked |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | Registration timestamp |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | Last update timestamp |
### Confirmation Token Entity
| Field | Type | Constraints | Description |
|-------|------|-------------|-------------|
| id | UUID v7 | PK, auto-generated | Token identifier |
| user_id | UUID | FK → users.id, NOT NULL | Owner reference |
| token | UUID | UNIQUE, NOT NULL | Confirmation token value |
| expires_at | TIMESTAMPTZ | NOT NULL | Expiration (created_at + 24h) |
| used_at | TIMESTAMPTZ | NULLABLE | When token was used |
### Indexes
- `idx_users_email` ON users(email)
- `idx_confirmation_tokens_token` ON confirmation_tokens(token)
- `idx_confirmation_tokens_user_id` ON confirmation_tokens(user_id)
Шаг 7: Edge cases и сценарии ошибок
Этот раздел часто пропускают -- и именно здесь скрываются баги.
## Edge Cases and Error Scenarios
### Registration Edge Cases
1. Email with valid but unusual format ([email protected]) → Accept
2. Email with unicode characters → Reject with clear error
3. Password exactly 8 characters → Accept
4. Password 7 characters → Reject
5. Password with only lowercase → Reject
6. Passwords don't match → Reject with field-specific error
7. Concurrent registration with same email → First wins, second gets 409
8. Re-registration of unconfirmed user → Resend confirmation email
9. Registration with email of blocked user → Reject with generic error (security)
### Confirmation Edge Cases
1. Token used twice → 400 "Token already used"
2. Token expired → 400 "Token expired, please re-register"
3. Valid token for blocked user → 400 "Account is not eligible"
4. Token with extra whitespace in URL → Trim and validate
Шаг 8: Стратегия тестирования
## Testing Strategy
### Unit Tests
- Email validation logic
- Password strength validation
- Token generation and verification
- User entity creation
### Integration Tests
- Full registration flow (register → confirm → login)
- Duplicate email handling
- Rate limiting verification
- Database constraints verification
### Edge Case Tests
- All scenarios from "Edge Cases" section
- Concurrent registration stress test
- Token expiration boundary test (23h59m vs 24h01m)
Инструменты для создания спецификаций
| Инструмент | Назначение | Когда использовать |
|---|---|---|
| Markdown | Текстовые спецификации | Всегда -- основной формат |
| OpenAPI/Swagger | API-контракты | Любой REST API |
| Mermaid | Диаграммы в markdown | Архитектура, потоки данных, состояния |
| JSON Schema | Валидация данных | Сложные структуры данных |
| PlantUML | UML-диаграммы | Классы, последовательности |
| dbdiagram.io | ER-диаграммы | Модели данных |
graph TD
A[User submits form] --> B{Validate input}
B -->|Invalid| C[Return 400 with errors]
B -->|Valid| D{Check email exists}
D -->|Exists + Active| E[Return 409 Conflict]
D -->|Exists + Pending| F[Resend confirmation]
D -->|Not exists| G[Create user]
G --> H[Generate token]
H --> I[Send confirmation email]
I --> J[Return 201 Created]
Gate 1: Ревью спецификации
Спецификация готова. Перед переходом к планированию -- гейт. Это проверка по чек-листу.
| Проверка | Вопрос | Статус |
|---|---|---|
| Полнота | Все требования покрыты? | |
| Однозначность | Можно ли трактовать двояко? | |
| Тестируемость | Каждое требование можно проверить? | |
| Реализуемость | Технически возможно в текущем стеке? | |
| Согласованность | Требования не противоречат друг другу? | |
| Edge cases | Граничные случаи описаны? | |
| API-контракт | Все эндпоинты полностью описаны? | |
| Модели данных | Все сущности и связи определены? |
Правило гейта: Если хотя бы один пункт не закрыт -- спецификация возвращается на доработку. Нет исключений. Один час на доработку спецификации экономит день на исправление реализации.
Кто проводит ревью:
- Тимлид / архитектор -- для средних и крупных фич
- Коллега-разработчик -- для небольших фич (peer review)
- Самопроверка по чек-листу -- для сольных проектов
Фаза 2: PLAN -- планирование реализации
Цель фазы
Создать упорядоченный план реализации, который разбивает спецификацию на логические шаги с учетом зависимостей. План отвечает на вопрос: "В каком порядке строить?"
Шаги создания плана
Шаг 1: Идентификация компонентов
Из спецификации извлекаем все компоненты, которые нужно создать или изменить.
## Components to Create/Modify
### New Components
1. Migration: create users table
2. Migration: create confirmation_tokens table
3. Entity: User
4. Entity: ConfirmationToken
5. Repository: UserRepository
6. Repository: ConfirmationTokenRepository
7. Service: RegistrationService
8. Service: EmailConfirmationService
9. Controller: RegistrationController
10. Controller: ConfirmationController
11. Message: SendConfirmationEmailMessage
12. Handler: SendConfirmationEmailHandler
13. Email template: confirmation.html.twig
14. Validator: PasswordStrength (custom constraint)
15. Rate Limiter: registration_limiter
16. Tests: unit tests for services
17. Tests: integration tests for controllers
18. OpenAPI documentation
### Modified Components
19. security.yaml: add public access for registration endpoints
20. messenger.yaml: routing for confirmation email message
Шаг 2: Определение зависимостей
Каждый компонент зависит от других. Нельзя создать Repository без Entity. Нельзя создать Service без Repository. Зависимости формируют направленный ациклический граф (DAG).
graph TD
M1[Migration: users] --> E1[Entity: User]
M2[Migration: tokens] --> E2[Entity: ConfirmationToken]
E1 --> R1[Repository: UserRepository]
E2 --> R2[Repository: TokenRepository]
E1 --> E2
R1 --> S1[Service: RegistrationService]
R2 --> S2[Service: EmailConfirmationService]
S1 --> C1[Controller: RegistrationController]
S2 --> C2[Controller: ConfirmationController]
S1 --> MSG[Message: SendConfirmationEmail]
MSG --> H[Handler: SendConfirmationEmailHandler]
H --> TPL[Email Template]
C1 --> T1[Tests: Registration]
C2 --> T2[Tests: Confirmation]
Шаг 3: Определение порядка
Из DAG вычисляем порядок. Компоненты без зависимостей идут первыми. Компоненты с удовлетворенными зависимостями -- следующими.
## Implementation Order
### Step 1: Database Layer (no dependencies)
1.1. Migration: create users table
1.2. Migration: create confirmation_tokens table
### Step 2: Domain Layer (depends on Step 1)
2.1. Entity: User
2.2. Entity: ConfirmationToken
2.3. Validator: PasswordStrength
### Step 3: Repository Layer (depends on Step 2)
3.1. Repository: UserRepository
3.2. Repository: ConfirmationTokenRepository
### Step 4: Service Layer (depends on Step 3)
4.1. Service: RegistrationService
4.2. Service: EmailConfirmationService
4.3. Message: SendConfirmationEmailMessage
4.4. Handler: SendConfirmationEmailHandler
### Step 5: Presentation Layer (depends on Step 4)
5.1. Controller: RegistrationController
5.2. Controller: ConfirmationController
5.3. Email Template
### Step 6: Configuration (depends on Steps 4-5)
6.1. security.yaml updates
6.2. messenger.yaml routing
6.3. Rate limiter configuration
### Step 7: Testing (depends on Steps 1-6)
7.1. Unit tests for validators
7.2. Unit tests for services
7.3. Integration tests for controllers
7.4. Edge case tests
### Step 8: Documentation (depends on Step 7)
8.1. OpenAPI spec finalization
8.2. README updates
Шаг 4: Оценка сложности
Каждый шаг оценивается по сложности. Это помогает понять, где AI справится автономно, а где потребуется больше контроля.
| Шаг | Компонент | Сложность | AI-автономность | Примечание |
|---|---|---|---|---|
| 1.1 | Migration: users | Низкая | Высокая | Стандартный SQL |
| 1.2 | Migration: tokens | Низкая | Высокая | Стандартный SQL |
| 2.1 | Entity: User | Низкая | Высокая | Doctrine mapping |
| 2.3 | Validator | Средняя | Средняя | Custom constraint |
| 4.1 | RegistrationService | Средняя | Средняя | Бизнес-логика |
| 5.1 | Controller | Низкая | Высокая | Тонкий контроллер |
| 6.3 | Rate limiter | Средняя | Средняя | Конфигурация Symfony |
| 7.3 | Integration tests | Высокая | Низкая | Требует ревью |
Правило: Чем ниже AI-автономность, тем подробнее должно быть описание задачи и тем тщательнее ревью результата.
Шаг 5: Анализ рисков
## Risks and Mitigations
### Risk 1: Email delivery failures
- **Probability**: Medium
- **Impact**: Users stuck in "pending" state
- **Mitigation**: Async email via Messenger, retry 3 times,
admin endpoint to resend manually
### Risk 2: Race condition on duplicate email check
- **Probability**: Low
- **Impact**: Duplicate user creation
- **Mitigation**: UNIQUE constraint in database (ultimate guard),
application-level check for user-friendly error message
### Risk 3: Token brute-force attack
- **Probability**: Medium
- **Impact**: Unauthorized account activation
- **Mitigation**: UUID v4 tokens (122 bits of entropy),
rate limiting on confirmation endpoint
### Risk 4: bcrypt performance under load
- **Probability**: Low (with cost factor 12)
- **Impact**: Slow registration responses
- **Mitigation**: Async password hashing if needed,
load testing before release
План как DAG
Почему план -- это именно DAG (направленный ациклический граф), а не линейный список?
Линейный список: DAG:
1 → 2 → 3 → 4 → 5 1 ──→ 3 ──→ 5
↓ ↑
2 ──→ 4 ────┘
Линейный список заставляет выполнять задачи строго одна за другой. DAG показывает, что некоторые задачи можно выполнять параллельно (задачи 1 и 2 не зависят друг от друга). Это критически важно:
- Параллелизация -- AI может работать над несколькими независимыми задачами
- Гибкость -- если одна задача заблокирована, можно работать над другой
- Минимизация простоев -- нет ожидания завершения независимых задач
## Parallelization Opportunities
Tasks that can run in parallel:
- Step 1.1 (users migration) || Step 1.2 (tokens migration)
- Step 2.1 (User entity) || Step 2.3 (PasswordStrength validator)
- Step 3.1 (UserRepository) || Step 3.2 (TokenRepository)
- Step 7.1 (unit tests) || Step 7.3 (integration tests scaffolding)
AI-ассистированное планирование
Спецификация готова -- используйте AI для создания плана. Но не слепо принимайте предложенный план; используйте его как отправную точку.
## Промпт для AI-планирования
I have a specification for a user registration feature (attached).
Create an implementation plan with the following structure:
1. List all components that need to be created
2. Define dependencies between components as a DAG
3. Order the implementation steps
4. Estimate complexity (low/medium/high) for each step
5. Identify which steps can be parallelized
6. Note risks and mitigations
Use our stack: Symfony 7.4, PHP 8.4, PostgreSQL 18, Redis 7.
Follow our conventions: UUID v7 for IDs, Messenger for async,
final classes by default, readonly DTOs.
AI генерирует план, который вы ревьюите и корректируете:
- Проверьте: все ли компоненты из спецификации покрыты?
- Проверьте: правильны ли зависимости?
- Проверьте: нет ли циклических зависимостей?
- Добавьте: шаги, которые AI мог пропустить (конфигурация, миграции, тесты)
- Скорректируйте: оценки сложности на основе вашего знания кодовой базы
Gate 2: Ревью плана
| Проверка | Вопрос |
|---|---|
| Покрытие спецификации | Каждое требование из спецификации покрыто шагом плана? |
| Корректность зависимостей | Ни один шаг не ссылается на несуществующий компонент? |
| Отсутствие циклов | DAG действительно ациклический? |
| Полнота | Конфигурация, миграции, тесты, документация -- всё включено? |
| Реалистичность оценок | Сложность оценена адекватно? |
| Риски | Основные риски идентифицированы, есть план митигации? |
Правило гейта: План должен быть таким, чтобы любой разработчик (или AI) мог взять его и понять, что нужно делать, в каком порядке, и как проверить результат.
Пример: от спецификации до плана
Рассмотрим сквозной пример -- от момента "нужна регистрация" до утвержденного плана.
Начало: запрос от бизнеса
"Пользователи должны иметь возможность создавать аккаунты
на платформе для отслеживания прогресса обучения."
Спецификация (сокращенная версия)
# Feature: User Registration
## Problem
Platform users cannot track learning progress without accounts.
## Success Criteria
- Users register with email + password
- Email confirmation required
- Login after confirmation
- API response < 200ms
## Functional Requirements
- FR-1: Registration (email, password, confirmation)
- FR-2: Email confirmation (token, 24h expiry)
- FR-3: Login (JWT tokens)
## API Contract
- POST /api/v1/auth/register → 201/400/409/429
- GET /api/v1/auth/confirm/{token} → 200/400/404
- POST /api/v1/auth/login → 200/401
## Data Models
- users (id, email, password_hash, status, timestamps)
- confirmation_tokens (id, user_id, token, expires_at, used_at)
План реализации
# Implementation Plan: User Registration
## Step 1: Database (Day 1)
- 1.1 Migration: users table [Low, AI: High]
- 1.2 Migration: confirmation_tokens table [Low, AI: High]
## Step 2: Domain (Day 1)
- 2.1 Entity: User [Low, AI: High]
- 2.2 Entity: ConfirmationToken [Low, AI: High]
- 2.3 DTO: RegisterRequest [Low, AI: High]
## Step 3: Repository (Day 1-2)
- 3.1 UserRepository: findByEmail, save [Low, AI: High]
- 3.2 TokenRepository: findByToken, save [Low, AI: High]
## Step 4: Business Logic (Day 2)
- 4.1 RegistrationService [Medium, AI: Medium]
- 4.2 EmailConfirmationService [Medium, AI: Medium]
- 4.3 Async email message + handler [Medium, AI: Medium]
## Step 5: API (Day 2-3)
- 5.1 RegistrationController [Low, AI: High]
- 5.2 ConfirmationController [Low, AI: High]
- 5.3 Security config [Low, AI: High]
## Step 6: Testing (Day 3)
- 6.1 Unit tests [Medium, AI: Medium]
- 6.2 Integration tests [High, AI: Low]
## Dependencies
1 → 2 → 3 → 4 → 5 → 6
1.1 || 1.2, 2.1 || 2.2 || 2.3
## Risks
- Email delivery: mitigate with Messenger retry
- Race condition: mitigate with DB unique constraint
После Gate 2
План утвержден. Можно переходить к фазе Tasks -- декомпозиции на атомарные задачи для AI. Об этом -- в следующей статье.
Чек-лист фазы Specify
- Проблема четко сформулирована
- Критерии успеха измеримы
- Функциональные требования пронумерованы
- Нефункциональные требования определены
- API-контракты описаны (OpenAPI)
- Модели данных определены
- Edge cases перечислены
- Стратегия тестирования описана
- Gate 1 пройден
Чек-лист фазы Plan
- Все компоненты идентифицированы
- Зависимости определены (DAG)
- Порядок реализации установлен
- Сложность оценена
- Возможности параллелизации отмечены
- Риски проанализированы
- Gate 2 пройден
Итоги
Фазы Specify и Plan -- это фундамент SDD. Спецификация фиксирует замысел, план определяет путь реализации. Без них AI-разработка превращается в бесконечный цикл "промпт → разочарование → переделка".
Главный вывод: Время, потраченное на спецификацию и план, окупается многократно. Час на спецификацию экономит день на реализации. День на планирование экономит неделю на отладке. Это не преувеличение -- это опыт команд, работающих по SDD.