Почему паттерны важны
Не все спецификации одинаково полезны для AI. Одна и та же фича может быть описана десятком способов -- и AI выдаст радикально разный результат в зависимости от формата спецификации.
Паттерны спецификаций -- это проверенные шаблоны, которые систематически производят предсказуемый и качественный выход от AI. Выбор правильного паттерна зависит от типа фичи, сложности и того, какой аспект системы наиболее критичен.
Паттерн спецификации Тип фичи
───────────────────── ──────────
API-First → REST/GraphQL эндпоинты
Test-First → Бизнес-логика, алгоритмы
State Machine → Сущности с жизненным циклом
Contract → Критичные интерфейсы
Example-Driven → Сложные трансформации данных
Constraint → Безопасность, производительность
Ключевой инсайт: AI-модели генерируют код на основе статистических паттернов. Чем ближе ваша спецификация к формату, который AI "видел" в обучающих данных, тем качественнее результат. OpenAPI-спецификация -- это формат, на котором тренировались все большие модели. BDD-сценарии -- тоже. Авторский свободный текст -- нет.
Паттерн 1: API-First Spec
Суть
Определите API-контракт до реализации. OpenAPI-схема становится и спецификацией, и документацией, и основой для генерации кода.
Когда использовать
- REST API endpoints
- Микросервисные интеграции
- Публичные API
- Когда frontend и backend разрабатываются параллельно
Как это работает
# specs/api/user-registration.yaml
openapi: 3.1.0
info:
title: User Registration API
version: 1.0.0
description: |
Handles user registration with email confirmation.
Rate limited to 5 attempts per IP per hour.
paths:
/api/v1/auth/register:
post:
operationId: registerUser
summary: Register a new user account
tags: [Authentication]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegisterRequest'
examples:
valid:
summary: Valid registration
value:
email: "[email protected]"
password: "SecurePass123"
password_confirmation: "SecurePass123"
invalid_email:
summary: Invalid email format
value:
email: "not-an-email"
password: "SecurePass123"
password_confirmation: "SecurePass123"
responses:
'201':
description: User created, confirmation email sent
content:
application/json:
schema:
$ref: '#/components/schemas/UserResponse'
example:
id: "01910f9a-7b4e-7d5a-8c3b-2e4f6a8b0c1d"
email: "[email protected]"
status: "pending_confirmation"
created_at: "2026-03-07T10:30:00Z"
'400':
$ref: '#/components/responses/ValidationError'
'409':
description: Email already registered
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: 409
message: "Email already registered"
'429':
description: Rate limit exceeded
headers:
Retry-After:
schema:
type: integer
description: Seconds until rate limit resets
components:
schemas:
RegisterRequest:
type: object
required: [email, password, password_confirmation]
properties:
email:
type: string
format: email
maxLength: 255
password:
type: string
minLength: 8
maxLength: 128
description: "Must contain uppercase, lowercase, and digit"
password_confirmation:
type: string
UserResponse:
type: object
properties:
id:
type: string
format: uuid
email:
type: string
format: email
status:
type: string
enum: [pending_confirmation, active, blocked]
created_at:
type: string
format: date-time
Error:
type: object
properties:
code:
type: integer
message:
type: string
responses:
ValidationError:
description: Validation failed
content:
application/json:
schema:
type: object
properties:
code:
type: integer
example: 400
message:
type: string
example: "Validation failed"
errors:
type: object
additionalProperties:
type: array
items:
type: string
Промпт для AI
Given this OpenAPI specification (attached), generate:
1. Symfony controller implementing all endpoints
2. Request DTO classes with validation attributes
3. Response DTO classes
4. Integration tests verifying all response codes and schemas
Follow conventions:
- PHP 8.4, strict_types
- Final classes, constructor property promotion
- Symfony Validator attributes on DTOs
- PHPUnit integration tests using WebTestCase
Преимущества API-First
| Преимущество | Описание |
|---|---|
| Однозначность | JSON Schema не допускает двойных трактовок |
| Генерация кода | Множество инструментов генерируют код из OpenAPI |
| Документация | Swagger UI автоматически из той же спецификации |
| Тестирование | Можно валидировать ответы против схемы |
| Параллелизм | Frontend работает по контракту до готовности backend |
Паттерн 2: Test-First Spec
Суть
Определите поведение как набор тестов до реализации. AI пишет код, чтобы тесты прошли. По сути -- TDD, где тесты и есть спецификация.
Когда использовать
- Чистая бизнес-логика без внешних зависимостей
- Алгоритмы и расчёты
- Утилитарные функции
- Валидаторы и трансформаторы данных
Как это работает
<?php
declare(strict_types=1);
namespace Tests\Unit\Service;
use App\Service\PasswordStrengthValidator;
use App\Exception\WeakPasswordException;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
/**
* SPEC: Password must be 8+ characters, contain at least
* one uppercase letter, one lowercase letter, and one digit.
* Special characters are allowed but not required.
*/
final class PasswordStrengthValidatorTest extends TestCase
{
private PasswordStrengthValidator $validator;
protected function setUp(): void
{
$this->validator = new PasswordStrengthValidator();
}
// --- Valid passwords ---
#[Test]
public function accepts_password_meeting_all_criteria(): void
{
// 8+ chars, uppercase, lowercase, digit
$this->assertTrue($this->validator->isValid('SecurePass1'));
}
#[Test]
public function accepts_password_with_special_characters(): void
{
$this->assertTrue($this->validator->isValid('S3cure!@#'));
}
#[Test]
public function accepts_password_exactly_8_characters(): void
{
$this->assertTrue($this->validator->isValid('Abcdef1x'));
}
#[Test]
public function accepts_very_long_password(): void
{
$password = str_repeat('Aa1', 42) . 'x'; // 127 chars
$this->assertTrue($this->validator->isValid($password));
}
// --- Invalid passwords ---
#[Test]
public function rejects_password_shorter_than_8_characters(): void
{
$this->assertFalse($this->validator->isValid('Abcde1'));
}
#[Test]
public function rejects_password_without_uppercase(): void
{
$this->assertFalse($this->validator->isValid('abcdefg1'));
}
#[Test]
public function rejects_password_without_lowercase(): void
{
$this->assertFalse($this->validator->isValid('ABCDEFG1'));
}
#[Test]
public function rejects_password_without_digit(): void
{
$this->assertFalse($this->validator->isValid('Abcdefgh'));
}
#[Test]
public function rejects_empty_password(): void
{
$this->assertFalse($this->validator->isValid(''));
}
// --- Error messages ---
#[Test]
public function returns_all_violation_messages(): void
{
$violations = $this->validator->validate('abc');
$this->assertContains('Password must be at least 8 characters', $violations);
$this->assertContains('Password must contain at least one uppercase letter', $violations);
$this->assertContains('Password must contain at least one digit', $violations);
}
#[Test]
public function returns_empty_array_for_valid_password(): void
{
$violations = $this->validator->validate('SecurePass1');
$this->assertEmpty($violations);
}
// --- Edge cases ---
#[Test]
public function handles_unicode_characters(): void
{
// Unicode letters should not count as uppercase/lowercase latin
$this->assertFalse($this->validator->isValid('пароль1А'));
}
#[Test]
public function rejects_password_exceeding_128_characters(): void
{
$password = str_repeat('Aa1', 43); // 129 chars
$this->assertFalse($this->validator->isValid($password));
}
}
Промпт для AI
Here are the tests for PasswordStrengthValidator (attached).
Implement the class so that ALL tests pass.
Requirements:
- src/Service/PasswordStrengthValidator.php
- Final class, no dependencies
- Methods: isValid(string): bool, validate(string): array
- validate() returns array of violation message strings
- PHP 8.4, strict_types
Do NOT modify the tests.
Преимущества Test-First
| Преимущество | Описание |
|---|---|
| Верификация встроена | Тесты -- одновременно спека и проверка |
| Однозначность | Тест либо проходит, либо нет |
| Edge cases | Граничные случаи зафиксированы как тесты |
| Regression | Тесты остаются как защита от регрессии |
| Понятность AI | "Make tests pass" -- самый чёткий промпт |
Паттерн 3: State Machine Spec
Суть
Определите систему как набор состояний и переходов между ними. Чёткие правила: какие переходы допустимы, что их вызывает, какие побочные эффекты возникают.
Когда использовать
- Сущности с жизненным циклом (заказ, документ, задача)
- Workflow-системы
- Конечные автоматы в бизнес-логике
- Системы с явными статусами
Как это работает
# State Machine: Order Lifecycle
## States
| State | Description | Terminal |
|-------|-------------|----------|
| `created` | Order placed, awaiting payment | No |
| `paid` | Payment received | No |
| `processing` | Being prepared for shipment | No |
| `shipped` | Handed to courier | No |
| `delivered` | Customer received | Yes |
| `cancelled` | Order cancelled | Yes |
| `refunded` | Payment returned | Yes |
## Transitions
| From | To | Trigger | Guard | Side Effect |
|------|----|---------|-------|-------------|
| `created` | `paid` | PaymentReceived | amount == order.total | SendConfirmationEmail |
| `created` | `cancelled` | CancelOrder | — | ReleaseInventory |
| `paid` | `processing` | StartProcessing | inventory.available | ReserveInventory |
| `paid` | `refunded` | RefundOrder | — | ProcessRefund, ReleaseInventory |
| `processing` | `shipped` | ShipOrder | tracking_number present | SendShippingNotification |
| `processing` | `cancelled` | CancelOrder | — | ProcessRefund, ReleaseInventory |
| `shipped` | `delivered` | ConfirmDelivery | — | — |
| `shipped` | `refunded` | RefundOrder | within 14 days | ProcessRefund |
## Forbidden Transitions
- `delivered` → any (terminal state)
- `cancelled` → any (terminal state)
- `refunded` → any (terminal state)
- `created` → `shipped` (must go through paid + processing)
- `paid` → `delivered` (must go through processing + shipped)
## Invariants
- Order total never changes after creation
- State transition timestamp is always recorded
- Only one active state at a time
- State history is immutable (append-only)
stateDiagram-v2
[*] --> created
created --> paid : PaymentReceived
created --> cancelled : CancelOrder
paid --> processing : StartProcessing
paid --> refunded : RefundOrder
processing --> shipped : ShipOrder
processing --> cancelled : CancelOrder
shipped --> delivered : ConfirmDelivery
shipped --> refunded : RefundOrder
delivered --> [*]
cancelled --> [*]
refunded --> [*]
Промпт для AI
Implement this state machine (attached) for the Order entity.
Requirements:
1. Order entity with status field (enum)
2. OrderStateMachine service with transition() method
3. Guards as separate validator classes
4. Side effects dispatched via MessageBus (async)
5. State history tracked in order_state_history table
6. Forbidden transitions throw InvalidTransitionException
Stack: Symfony 7.4, PHP 8.4 enums, Messenger for async.
Use Symfony Workflow component as the underlying engine.
Преимущества State Machine Spec
| Преимущество | Описание |
|---|---|
| Полнота | Все возможные состояния и переходы явно определены |
| Запреты | Явно указано, что НЕ может произойти |
| Тестируемость | Каждый переход -- отдельный тест-кейс |
| Визуализация | Диаграмма сразу видна (Mermaid/PlantUML) |
| AI-дружелюбность | Таблица переходов -- идеальный формат для AI |
Паттерн 4: Contract Spec
Суть
Определите контракты для каждого компонента: предусловия (preconditions), постусловия (postconditions), инварианты. Подход Design by Contract Бертрана Мейера.
Когда использовать
- Критичные бизнес-компоненты
- Финансовые расчеты
- Интерфейсы между модулями
- Когда корректность важнее производительности
Как это работает
# Contract: MoneyTransferService
## Method: transfer(from, to, amount)
### Preconditions (REQUIRE)
- `from` account exists and is active
- `to` account exists and is active
- `from` != `to` (no self-transfer)
- `amount` > 0
- `amount` <= `from.balance`
- `from.currency` == `to.currency` (same currency)
- No pending transfer for same from/to/amount (idempotency)
### Postconditions (ENSURE)
- `from.balance` decreased by exactly `amount`
- `to.balance` increased by exactly `amount`
- `from.balance` + `to.balance` == old(from.balance) + old(to.balance)
(conservation of money)
- Transfer record created with status "completed"
- AuditLog entry created
### Invariants
- Account balance is never negative
- Sum of all account balances is constant (closed system)
- Every transfer has exactly two ledger entries (debit + credit)
- Ledger entries are immutable after creation
### Error conditions
- InsufficientFundsException: amount > from.balance
- AccountNotFoundException: from or to doesn't exist
- SamAccountException: from == to
- InvalidAmountException: amount <= 0
- CurrencyMismatchException: different currencies
- DuplicateTransferException: idempotency violation
Промпт для AI
Implement MoneyTransferService according to these contracts (attached).
Requirements:
1. All preconditions checked at method entry, throw specific exceptions
2. All postconditions verified via assertions after execution
3. Invariants checked in a separate InvariantChecker that runs
in dev/test environments
4. Database transaction wraps the entire transfer
5. Pessimistic locking on both accounts (ORDER BY id to prevent deadlock)
Stack: Symfony 7.4, PHP 8.4, Doctrine ORM, PostgreSQL.
<?php
declare(strict_types=1);
namespace App\Service;
use App\Entity\Account;
use App\Exception\InsufficientFundsException;
use App\Exception\InvalidAmountException;
use App\Exception\SameAccountException;
final class MoneyTransferService
{
// Contract: transfer money between accounts
//
// REQUIRE: fromAccount.isActive && toAccount.isActive
// REQUIRE: fromAccount.id != toAccount.id
// REQUIRE: amount > 0
// REQUIRE: amount <= fromAccount.balance
// REQUIRE: fromAccount.currency == toAccount.currency
//
// ENSURE: fromAccount.balance == old(fromAccount.balance) - amount
// ENSURE: toAccount.balance == old(toAccount.balance) + amount
// ENSURE: transferRecord.status == 'completed'
//
// INVARIANT: fromAccount.balance >= 0
// INVARIANT: sum(all_balances) == const
public function transfer(Account $from, Account $to, int $amount): Transfer
{
// Check preconditions
$this->checkPreconditions($from, $to, $amount);
$oldFromBalance = $from->getBalance();
$oldToBalance = $to->getBalance();
// Execute transfer
$from->debit($amount);
$to->credit($amount);
$transfer = new Transfer($from, $to, $amount);
// Verify postconditions
assert($from->getBalance() === $oldFromBalance - $amount);
assert($to->getBalance() === $oldToBalance + $amount);
assert($from->getBalance() + $to->getBalance() === $oldFromBalance + $oldToBalance);
return $transfer;
}
}
Преимущества Contract Spec
| Преимущество | Описание |
|---|---|
| Математическая строгость | Контракты можно формально верифицировать |
| Самодокументирование | Контракты в коде -- живая документация |
| Раннее обнаружение ошибок | Precondition violations ловят баги на входе |
| AI-понятность | Чёткая структура REQUIRE/ENSURE/INVARIANT |
Паттерн 5: Example-Driven Spec
Суть
Опишите поведение через конкретные примеры входных и выходных данных. AI обобщает паттерн из примеров. Близко к BDD (Behavior-Driven Development) с форматом Given/When/Then.
Когда использовать
- Сложные трансформации данных
- Бизнес-правила с множеством вариаций
- Когда формальное описание правил слишком сложное
- Когда stakeholders мыслят примерами, а не абстракциями
Как это работает
# Feature: Price Calculator
## Scenario 1: Simple purchase without discounts
**Given** products in cart:
| Product | Price | Quantity |
|---------|-------|----------|
| Widget | 1000 | 2 |
**When** calculating total
**Then** result should be:
| Subtotal | Discount | Tax (20%) | Total |
|----------|----------|-----------|-------|
| 2000 | 0 | 400 | 2400 |
## Scenario 2: Quantity discount (10+ items = 10% off)
**Given** products in cart:
| Product | Price | Quantity |
|---------|-------|----------|
| Widget | 1000 | 15 |
**When** calculating total
**Then** result should be:
| Subtotal | Discount | Tax (20%) | Total |
|----------|----------|-----------|--------|
| 15000 | 1500 | 2700 | 16200 |
## Scenario 3: Multiple products with mixed discounts
**Given** products in cart:
| Product | Price | Quantity |
|----------|-------|----------|
| Widget | 1000 | 15 |
| Gadget | 5000 | 3 |
**And** promo code "SAVE20" applied (20% off gadgets)
**When** calculating total
**Then** result should be:
| Line | Subtotal | Line Discount | After Discount |
|----------|----------|---------------|----------------|
| Widget | 15000 | 1500 (qty) | 13500 |
| Gadget | 15000 | 3000 (promo) | 12000 |
| Combined | Discount | Tax (20%) | Total |
|----------|----------|-----------|--------|
| 30000 | 4500 | 5100 | 30600 |
## Scenario 4: Edge case — empty cart
**Given** empty cart
**When** calculating total
**Then** result should be:
| Subtotal | Discount | Tax | Total |
|----------|----------|-----|-------|
| 0 | 0 | 0 | 0 |
## Scenario 5: Edge case — discount larger than subtotal
**Given** products in cart:
| Product | Price | Quantity |
|---------|-------|----------|
| Widget | 100 | 1 |
**And** promo code "FLAT200" applied (200 flat discount)
**When** calculating total
**Then** result should be:
| Subtotal | Discount | Tax | Total |
|----------|----------|-----|-------|
| 100 | 100 | 0 | 0 |
Note: Discount capped at subtotal, total never negative
Промпт для AI
Implement a PriceCalculator service that produces
the results shown in these scenarios (attached).
Requirements:
1. Accept cart items (product, price, quantity)
2. Accept optional promo codes
3. Apply quantity discounts (10+ items = 10% off that line)
4. Apply promo code discounts
5. Calculate tax at 20% on discounted subtotal
6. Return detailed breakdown (per-line and total)
7. All money in integer cents (no floating point)
Write the service AND tests that verify all 5 scenarios.
Преимущества Example-Driven
| Преимущество | Описание |
|---|---|
| Понятность | Примеры понятны даже не-техническим людям |
| Конкретность | Нет абстракций -- только входы и выходы |
| Тестируемость | Каждый сценарий = один тест |
| Полнота | Edge cases естественно включаются как сценарии |
| AI-эффективность | AI хорошо обобщает паттерны из примеров |
Паттерн 6: Constraint Spec
Суть
Определите, что система НЕ должна делать. Негативные требования так же важны, как позитивные -- и часто более критичны, потому что их нарушение ведёт к уязвимостям, утечкам данных и деградации производительности.
Когда использовать
- Требования безопасности
- Бюджеты производительности
- Compliance и регуляторные требования
- Запрещённые паттерны в коде
Как это работает
# Constraints: User API
## Security Constraints
### C-SEC-1: No raw SQL
- MUST NOT use raw SQL queries anywhere in the codebase
- All database access through Doctrine ORM/DBAL query builder
- Verification: grep for "->query(", "->exec(", "->executeQuery("
with raw string parameters
### C-SEC-2: No user-controlled data in logs
- MUST NOT log passwords, tokens, or PII
- Allowed in logs: user ID (UUID), email domain (not full email),
action type, timestamp
- Verification: PHPStan custom rule checking log calls
### C-SEC-3: No sensitive data in responses
- MUST NOT include password_hash in any API response
- MUST NOT include internal IDs (database serial) — only UUIDs
- MUST NOT include stack traces in production errors
- Verification: integration tests check response schemas
### C-SEC-4: Authentication required by default
- All endpoints MUST require authentication UNLESS explicitly
marked as public in security.yaml
- Public endpoints: /api/v1/auth/register, /api/v1/auth/login,
/api/v1/auth/confirm/{token}, /api/health
- Verification: security configuration audit, penetration test
## Performance Constraints
### C-PERF-1: Response time budget
- All API endpoints MUST respond within 200ms (p95)
- Database queries MUST complete within 50ms each
- No endpoint may make more than 5 database queries
- Verification: load testing with k6/wrk
### C-PERF-2: No N+1 queries
- MUST NOT perform queries inside loops
- All related data loaded via JOINs or batch queries
- Verification: Doctrine query counter in test environment
### C-PERF-3: Payload size limit
- API responses MUST NOT exceed 1MB
- List endpoints MUST use pagination (max 50 items per page)
- Verification: integration tests check Content-Length
## Code Quality Constraints
### C-CODE-1: No suppressed errors
- MUST NOT use @phpstan-ignore, @phpstan-ignore-next-line
- MUST NOT use error suppression operator (@)
- All type errors must be fixed, not suppressed
- Verification: grep for suppression patterns
### C-CODE-2: No magic numbers
- All numeric constants MUST be named (const or enum)
- Exception: 0, 1, -1 in obvious contexts
- Verification: code review checklist
### C-CODE-3: No service locator pattern
- MUST NOT use ContainerInterface::get() to fetch services
- All dependencies via constructor injection
- Verification: PHPStan rule, grep for "->get("
## Data Constraints
### C-DATA-1: No hard deletes
- MUST NOT DELETE user data (soft delete with deleted_at)
- Exception: temporary tokens (confirmation, password reset)
- Verification: migration review, grep for "DELETE FROM" without
token/session tables
### C-DATA-2: No OFFSET pagination
- MUST NOT use OFFSET for pagination
- Use keyset (cursor-based) pagination
- Verification: grep for "->setFirstResult(", "OFFSET"
Промпт для AI
Implement the User API following these constraints (attached).
When generating code:
1. Check each constraint BEFORE writing code
2. After implementation, verify each constraint is satisfied
3. Add inline comments referencing constraint IDs
where relevant (e.g., // C-SEC-1: using query builder)
If a constraint conflicts with a functional requirement,
flag it and ask for clarification.
Проверка constraints в CI
# .github/workflows/constraints.yml
name: Constraint Verification
on: [push, pull_request]
jobs:
security-constraints:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# C-SEC-1: No raw SQL
- name: Check for raw SQL
run: |
! grep -rn "->query\s*(" src/ --include="*.php" | \
grep -v "QueryBuilder"
# C-CODE-1: No suppressed errors
- name: Check for error suppression
run: |
! grep -rn "@phpstan-ignore" src/ --include="*.php"
! grep -rn "^\s*@\$" src/ --include="*.php"
# C-CODE-3: No service locator
- name: Check for service locator
run: |
! grep -rn "ContainerInterface" src/ --include="*.php" | \
grep -v "CompilerPass"
# C-DATA-2: No OFFSET pagination
- name: Check for OFFSET pagination
run: |
! grep -rn "setFirstResult\|OFFSET" src/ --include="*.php"
Преимущества Constraint Spec
| Преимущество | Описание |
|---|---|
| Безопасность | Явные запреты предотвращают уязвимости |
| Автоматизация | Constraints проверяемы в CI |
| Культура | Команда знает, что запрещено |
| AI-контроль | AI знает границы дозволенного |
Выбор паттерна: матрица решений
Не все паттерны подходят для всех задач. Используйте эту матрицу для выбора.
| Тип фичи | Рекомендуемый паттерн | Дополнительный паттерн |
|---|---|---|
| REST API эндпоинт | API-First | Constraint |
| Бизнес-логика (расчёты) | Test-First | Example-Driven |
| Сущность с жизненным циклом | State Machine | Contract |
| Финансовые операции | Contract | Constraint |
| Трансформация данных | Example-Driven | Test-First |
| Безопасность / compliance | Constraint | Contract |
| CRUD-операции | API-First | — |
| Интеграция с внешним API | Contract | API-First |
| Workflow / процессы | State Machine | Example-Driven |
| Валидация данных | Test-First | Constraint |
Комбинирование паттернов
Для сложных фич используйте несколько паттернов одновременно:
# Feature: Order System
## API Contract (API-First)
→ OpenAPI spec for all order endpoints
## Order Lifecycle (State Machine)
→ States and transitions diagram
## Pricing Rules (Example-Driven)
→ Scenarios with expected calculations
## Security (Constraint)
→ What the system MUST NOT do
## Payment Processing (Contract)
→ Pre/post conditions for financial operations
Практический совет: Начните с одного паттерна -- того, который лучше всего подходит для главного аспекта фичи. Добавляйте другие паттерны по мере необходимости. Не нужно использовать все шесть для каждой задачи.
Итоги
Паттерны спецификаций -- это ваш набор инструментов для общения с AI. Каждый паттерн оптимизирован для определённого типа задач:
- API-First -- когда контракт взаимодействия важнее реализации
- Test-First -- когда поведение важнее структуры
- State Machine -- когда состояния и переходы определяют систему
- Contract -- когда корректность критична
- Example-Driven -- когда правила лучше понятны через примеры
- Constraint -- когда запреты важнее разрешений
Мастерство в SDD -- это умение быстро выбрать правильный паттерн и написать спецификацию, которая даст AI максимум информации при минимуме двусмысленности.