MidТеория8 min

5 принципов Spec-Driven Development

Основополагающие принципы спецификационно-ориентированной разработки с AI

Что такое Spec-Driven Development (SDD)

Spec-Driven Development -- это методология разработки, в которой спецификация является первичным артефактом, а не код. Вместо того чтобы писать код и документировать его потом, вы сначала пишете детальную спецификацию, а затем используете AI для генерации кода по этой спецификации.

Фундаментальный сдвиг: "Мы переходим из мира, где код -- это источник истины, в мир, где намерение -- это источник истины."

Контекст и происхождение

SDD основан на исследованиях Addy Osmani (Google Chrome, автор "Learning JavaScript Design Patterns") и практическом опыте Enrico Papalini. Идея не нова -- Design by Contract, BDD, TDD уже предлагали "сначала определи, потом реализуй". Но AI делает SDD практически осуществимым: спецификация может быть автоматически преобразована в рабочий код.

Почему SDD работает с AI

Традиционный подход:
  Идея → Код → Документация (если будет время)
  Проблема: документация устаревает, intent теряется

SDD без AI:
  Идея → Спецификация → Код (вручную) → Верификация
  Проблема: медленно, спецификация не окупает затрат

SDD с AI:
  Идея → Спецификация → Код (AI) → Верификация (автоматическая)
  Преимущество: спецификация окупается мгновенно, intent сохраняется

AI меняет экономику: стоимость перехода от spec к коду падает практически до нуля. Это делает инвестицию в качественную спецификацию выгодной.

Принцип 1: Start with Vision, Let AI Draft Details

Суть принципа

Вы определяете "что" и "зачем". AI помогает с "как". Человек задаёт видение, ограничения и критерии приёмки. AI расширяет это в детальную техническую спецификацию, а затем в код.

Как это выглядит на практике

Шаг 1: Человек пишет vision (5-10 минут)

## Feature: Order Cancellation

### Business Context
Customers need to cancel orders within 30 minutes of placing them.
After 30 minutes, cancellation is not possible (order goes to fulfillment).
Cancelled orders should refund to original payment method.

### Success Criteria
- Customer can cancel order from order detail page
- Cancellation within 30 minutes: instant, no questions asked
- After 30 minutes: show message "Order cannot be cancelled"
- Refund initiated automatically on cancellation
- Email notification sent to customer on cancellation
- Admin can see cancelled orders with reason

### Constraints
- Must work with existing PaymentService
- Must not affect fulfillment pipeline
- Must be idempotent (double-click safe)

Шаг 2: AI расширяет в детальную спецификацию

## Technical Specification: Order Cancellation

### API Endpoints

#### POST /api/v1/orders/{id}/cancel
- **Auth**: ROLE_USER (must own the order)
- **Request Body**: `{ "reason": "string (optional, max 500 chars)" }`
- **Success Response**: 200
  ```json
  {
    "order_id": "uuid",
    "status": "cancelled",
    "refund_status": "initiated",
    "cancelled_at": "2026-03-07T10:00:00Z"
  }
  • Error Responses:
    • 404: Order not found
    • 403: Order belongs to another user
    • 409: Order cannot be cancelled (past 30-min window or already cancelled)
    • 422: Invalid reason (> 500 chars)

Data Model Changes

  • orders table: add cancelled_at TIMESTAMPTZ NULL
  • orders table: add cancellation_reason TEXT NULL
  • order_refunds table: new table for refund tracking

State Machine

placed → cancelled (within 30 min)
placed → processing (after 30 min, via cron)
cancelled → refunded (after payment processor confirms)

Edge Cases

  1. Order cancelled at exactly 30 minutes → Allow (inclusive)
  2. Payment refund fails → Mark as "refund_failed", alert admin
  3. Concurrent cancel + fulfill → Use optimistic locking
  4. Already cancelled order → Return current state (idempotent)

Events

  • OrderCancelled → triggers email notification
  • OrderCancelled → triggers refund via PaymentService
  • RefundCompleted → updates order refund status
  • RefundFailed → alerts admin via notification service

**Шаг 3: Человек ревьюит и одобряет спецификацию**

На этом этапе вы проверяете: все ли edge cases учтены? Правильна ли state machine? Нет ли конфликтов с существующей архитектурой?

**Шаг 4: AI реализует по одобренной спецификации**

> **Ключевой инсайт:** Проверить спецификацию значительно проще, чем проверить код. Спецификация написана на языке бизнеса, код -- на языке машины. Ошибку в спецификации увидит любой стейкхолдер.

## Принцип 2: Structure Like a Professional Document

### Суть принципа

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

### Почему структура критична

Неструктурированный промпт: "Сделай API для отмены заказов. Нужно чтобы можно было отменить в течение 30 минут, после нельзя. И рефанд."

→ AI угадывает детали, может упустить edge cases, формат ответов непредсказуем

Структурированная спецификация: Overview → Requirements → API Contract → Data Model → Edge Cases → Testing Strategy → Security

→ AI точно знает что реализовать, формат определён, edge cases покрыты


### Шаблон спецификации

```markdown
# Feature: [Name]

## 1. Overview
Brief description of the feature, its purpose, and business value.

## 2. Requirements

### 2.1 Functional Requirements
- FR-1: [Requirement with clear, testable criterion]
- FR-2: [...]

### 2.2 Non-Functional Requirements
- NFR-1: Response time < 200ms for p95
- NFR-2: Must handle 100 concurrent cancellations
- NFR-3: Data retention: cancelled orders kept for 7 years

## 3. API Contract

### 3.1 Endpoints
For each endpoint:
- Method + Path
- Authentication / Authorization
- Request schema (with examples)
- Response schema (with examples)
- Error responses (with codes and messages)

### 3.2 Events / Messages
- Event name, payload schema, consumers

## 4. Data Model

### 4.1 New Tables
- Table name, columns, types, constraints, indexes

### 4.2 Migrations
- Changes to existing tables
- Data migration requirements

## 5. Business Logic

### 5.1 Rules
- Rule-1: [Clear, unambiguous business rule]
- Rule-2: [...]

### 5.2 State Machine (if applicable)
- States, transitions, guards

## 6. Edge Cases
- EC-1: [Scenario] → [Expected behavior]
- EC-2: [...]

## 7. Security
- Authentication requirements
- Authorization rules
- Input validation
- Rate limiting

## 8. Testing Strategy

### 8.1 Unit Tests
- What to test, key scenarios

### 8.2 Integration Tests
- What to test, dependencies

### 8.3 Acceptance Tests
- Mapping to requirements

## 9. Out of Scope
- What this feature does NOT do
- Future considerations

Почему каждый раздел важен

Раздел Без него AI может...
Overview Неправильно понять контекст и цель
Requirements Упустить функциональность или добавить лишнее
API Contract Придумать несовместимый формат
Data Model Создать неэффективную схему
Business Logic Реализовать неправильные правила
Edge Cases Игнорировать граничные ситуации
Security Пропустить проверки безопасности
Testing Strategy Написать поверхностные тесты
Out of Scope Реализовать больше, чем нужно

Принцип 3: Keep Things Modular

Суть принципа

Разбивайте сложные фичи на независимые, тестируемые модули. Каждый модуль имеет свою спецификацию, свои тесты, свою верификацию.

Почему модульность критична для AI

Монолитная спецификация (плохо):
  1 спецификация на 50 страниц → 1 гигантский промпт → AI теряет контекст

Модульная спецификация (хорошо):
  10 спецификаций по 5 страниц → 10 фокусированных промптов → AI сохраняет контекст

AI-модели имеют ограниченное context window. Даже с 200K токенов -- длинные спецификации снижают качество генерации. Короткие, фокусированные модули дают лучший результат.

Декомпозиция на модули

Пример: фича "Order Cancellation" разбивается на модули:

feature: "Order Cancellation"
modules:
  - name: "cancellation-api"
    spec: "specs/order-cancellation/api.md"
    scope: "Controller, DTO, validation"
    dependencies: []
    tests: "Unit tests for validation, integration test for endpoint"

  - name: "cancellation-logic"
    spec: "specs/order-cancellation/business-logic.md"
    scope: "CancelOrderHandler, time window check, idempotency"
    dependencies: ["OrderRepository"]
    tests: "Unit tests for business rules, edge cases"

  - name: "cancellation-refund"
    spec: "specs/order-cancellation/refund.md"
    scope: "RefundService, PaymentService integration"
    dependencies: ["PaymentService", "OrderRepository"]
    tests: "Unit tests with mocked PaymentService, integration test"

  - name: "cancellation-notification"
    spec: "specs/order-cancellation/notification.md"
    scope: "Event listener, email template"
    dependencies: ["MailerService", "EventDispatcher"]
    tests: "Unit test for listener, template rendering test"

  - name: "cancellation-admin"
    spec: "specs/order-cancellation/admin.md"
    scope: "Admin panel: cancelled orders list, refund status"
    dependencies: ["AdminCRUD"]
    tests: "Functional test for admin page"

Преимущества модульного подхода

  1. Параллельная работа. Каждый модуль можно генерировать и тестировать независимо.
  2. Инкрементальная доставка. Можно выпустить API без админки, добавить позже.
  3. Лучшее качество AI. Фокусированный контекст = лучший результат.
  4. Проще ревью. 5 маленьких PR легче ревьюить, чем 1 огромный.
  5. Изоляция ошибок. Проблема в одном модуле не ломает остальные.

Анти-паттерн: монолитная спецификация

ПЛОХО:
  spec.md (2000 строк):
    - API endpoints (15 штук)
    - Data model (8 таблиц)
    - Business logic (20 правил)
    - Frontend (10 страниц)
    - Admin panel
    - Email templates
    - Cron jobs
    - Migrations

  → AI теряет фокус к середине документа
  → Связи между компонентами неявные
  → Невозможно тестировать инкрементально
  → PR на 3000 строк — nightmare для ревьюера

Принцип 4: Build In Guardrails

Суть принципа

Спецификация должна включать ограничения: что НЕ делать, требования безопасности, performance-бюджеты. Guardrails предотвращают типичные ошибки AI ещё на этапе генерации.

Типы guardrails в спецификации

1. Архитектурные ограничения:

## Constraints

### Architecture
- MUST use existing CQRS pattern (Command + Handler)
- MUST NOT add new dependencies (no new Composer packages)
- MUST use existing event bus (Symfony Messenger)
- MUST NOT create God-objects or Service Locator pattern

### Database
- MUST use parameterized queries (no string concatenation)
- MUST use existing repository pattern
- MUST add indexes for all foreign keys
- MUST NOT use raw SQL in controllers

2. Security-ограничения:

### Security
- MUST validate all input (type, length, format)
- MUST check authorization (user owns the resource)
- MUST NOT expose internal IDs in URLs (use UUIDs)
- MUST NOT log sensitive data (payment info, tokens)
- MUST rate limit: max 10 cancellations per user per hour
- MUST use CSRF protection for web forms

3. Performance-бюджеты:

### Performance
- API response time: < 200ms for p95
- Database queries per request: max 3
- Memory usage: < 32MB per request
- MUST NOT make synchronous HTTP calls to external services
  (use async via Messenger)
- MUST use pagination for list endpoints (max 50 items per page)

4. Запреты (что НЕ делать):

### Anti-patterns to avoid
- DO NOT use inheritance for Order types (use composition)
- DO NOT add caching at this stage (premature optimization)
- DO NOT create generic "BaseEntity" class
- DO NOT implement soft deletes (use status field)
- DO NOT add logging inside domain logic (use events)

Почему guardrails в спецификации эффективнее post-hoc проверок

Без guardrails:
  AI генерирует код → Ревьюер находит проблемы →
  Разработчик переделывает → Новый ревью →
  Итого: 2-3 итерации

С guardrails:
  AI генерирует код С УЧЁТОМ ограничений →
  Ревьюер подтверждает →
  Итого: 1 итерация (обычно)

Guardrails -- это shift-left для AI-генерации: проблемы предотвращаются на этапе создания, а не ловятся на этапе проверки.

Принцип 5: Iterate Forever

Суть принципа

Спецификации -- это живые документы, а не write-once артефакты. По мере реализации вы узнаёте новое, и спецификация должна обновляться вместе с кодом.

Цикл обратной связи spec-code

Spec v1 → AI Code v1 → Review → Learnings →
Spec v2 → AI Code v2 → Review → Learnings →
Spec v3 → AI Code v3 → Production

Каждая итерация добавляет знания:

Итерация Что узнали Обновление spec
v1 → v2 PaymentService не поддерживает partial refund Добавили constraint: "full refund only"
v2 → v3 30-минутное окно нужно считать от payment confirmation, не от order creation Уточнили бизнес-правило
v3 → production Нужен webhook от payment provider для refund status Добавили модуль webhook

Версионирование спецификаций

Спецификации должны версионироваться вместе с кодом:

project/
├── specs/
│   ├── order-cancellation/
│   │   ├── api.md              # Current version
│   │   ├── business-logic.md
│   │   ├── refund.md
│   │   └── CHANGELOG.md        # History of changes
│   ├── user-registration/
│   │   └── ...
│   └── ADR/                    # Architecture Decision Records
│       ├── 001-use-cqrs.md
│       └── 002-async-refunds.md
├── src/
│   └── ...
└── tests/
    └── ...
# CHANGELOG.md — Order Cancellation Spec

## v3 (2026-03-07)
- Added webhook module for refund status updates
- Changed time window: calculated from payment_confirmed_at (not created_at)
- Added rate limiting: max 10 cancellations per user per hour

## v2 (2026-03-05)
- Added constraint: full refund only (no partial)
- Added edge case: concurrent cancel + fulfill
- Updated state machine with refund_failed state

## v1 (2026-03-03)
- Initial specification
- Core: API, business logic, notification, admin

Когда обновлять спецификацию

spec_update_triggers:
  mandatory:
    - "Business rule changed"
    - "Edge case discovered during implementation"
    - "API contract changed (breaking change)"
    - "Security requirement added"
    - "Performance requirement changed"

  recommended:
    - "Implementation revealed simpler approach"
    - "New dependency introduced"
    - "Error handling improved based on testing"
    - "User feedback received"

  not_needed:
    - "Code style changes (formatting, naming)"
    - "Internal refactoring (same behavior)"
    - "Test improvements (same spec)"
    - "Performance optimization (same contract)"

Обнаружение "spec gap"

Spec gap -- ситуация, когда код разошёлся со спецификацией. Это опасно, потому что спецификация перестаёт быть надёжным источником истины.

Как обнаруживать:

spec_gap_detection:
  manual:
    - "Code review includes spec compliance check"
    - "Monthly spec-code audit for critical features"

  semi_automated:
    - "API contract tests (OpenAPI schema validation)"
    - "State machine tests match spec state diagram"
    - "Business rule tests reference spec requirement IDs"

  automated:
    - "OpenAPI spec → generated tests → run against implementation"
    - "Contract tests verify API matches spec"
    - "PR template includes checkbox: 'Spec updated if needed'"

Пример теста, привязанного к спецификации:

<?php

declare(strict_types=1);

/**
 * Tests for Order Cancellation spec v3
 * Spec: specs/order-cancellation/business-logic.md
 */
final class OrderCancellationTest extends TestCase
{
    /**
     * Spec requirement: FR-1
     * "Customer can cancel order within 30 minutes of payment confirmation"
     */
    public function testCancellationWithin30Minutes(): void
    {
        $order = OrderFactory::create(
            paymentConfirmedAt: new \DateTimeImmutable('-25 minutes')
        );

        $result = $this->handler->handle(new CancelOrderCommand($order->getId()));

        $this->assertTrue($result->isCancelled());
    }

    /**
     * Spec requirement: FR-1 (boundary)
     * "30 minutes is inclusive"
     */
    public function testCancellationAtExactly30Minutes(): void
    {
        $order = OrderFactory::create(
            paymentConfirmedAt: new \DateTimeImmutable('-30 minutes')
        );

        $result = $this->handler->handle(new CancelOrderCommand($order->getId()));

        $this->assertTrue($result->isCancelled());
    }

    /**
     * Spec requirement: FR-2
     * "After 30 minutes, cancellation is not possible"
     */
    public function testCancellationAfter30Minutes(): void
    {
        $order = OrderFactory::create(
            paymentConfirmedAt: new \DateTimeImmutable('-31 minutes')
        );

        $this->expectException(OrderCannotBeCancelledException::class);

        $this->handler->handle(new CancelOrderCommand($order->getId()));
    }

    /**
     * Spec edge case: EC-4
     * "Already cancelled order returns current state (idempotent)"
     */
    public function testIdempotentCancellation(): void
    {
        $order = OrderFactory::create(
            status: OrderStatus::Cancelled,
            cancelledAt: new \DateTimeImmutable('-5 minutes')
        );

        $result = $this->handler->handle(new CancelOrderCommand($order->getId()));

        $this->assertTrue($result->isCancelled());
        $this->assertEquals($order->getCancelledAt(), $result->getCancelledAt());
    }
}

Сводная таблица принципов

# Принцип Одним предложением Главный риск при нарушении
1 Start with Vision Человек -- "что/зачем", AI -- "как" AI решает не ту проблему
2 Structure Like a Pro Единый формат спецификации AI упускает требования, несогласованные форматы
3 Keep Things Modular Маленькие, независимые модули Потеря контекста, невозможность инкрементальной доставки
4 Build In Guardrails Ограничения прямо в спецификации AI допускает предотвратимые ошибки
5 Iterate Forever Спецификация -- живой документ Spec gap, устаревшая документация

Ключевые выводы

  1. SDD меняет экономику разработки. Инвестиция в спецификацию окупается мгновенно, потому что AI превращает spec в код за минуты, а не дни.

  2. Vision остаётся за человеком. AI отлично работает с "как", но "что" и "зачем" -- это ответственность разработчика и бизнеса.

  3. Структура -- это рычаг. Структурированная спецификация улучшает качество AI-генерации на порядок. Потратьте 30 минут на шаблон -- сэкономите часы на ревью.

  4. Модульность масштабирует AI. 10 маленьких спецификаций всегда лучше 1 большой. AI работает лучше с фокусированным контекстом.

  5. Guardrails дешевле фиксов. Предотвратить ошибку в spec -- 1 минута. Найти и исправить ошибку в коде -- 30 минут. Исправить ошибку в production -- 3 часа.

  6. Спецификация -- это не финальный документ. Iterate forever. Каждая итерация добавляет знания. Spec gap -- это техдолг документации.