Зачем это нужно
В первом модуле мы увидели, почему one-shot development теряет управляемость: большая задача содержит много скрытых решений, а агент начинает угадывать. Specification-Driven Development начинается с простого поворота мышления: перед тем как просить AI писать код, нужно описать наблюдаемое поведение системы.
Specification нужна не для бюрократии. Она нужна, чтобы:
- отделить product decision от реализации;
- превратить пожелание в проверяемое поведение;
- дать агенту source of truth;
- уменьшить число скрытых assumptions;
- связать требования с acceptance criteria и тестами.
Если specification слабая, агент может написать много аккуратного кода, который реализует не то поведение.
Основная идея
Specification описывает observable behavior: что должно произойти, при каких условиях и как мы поймем, что результат правильный.
Плохо:
Сделай хороший authentication.
Почему плохо:
- непонятно, какие endpoints входят в задачу;
- непонятно, какое поведение считается правильным;
- непонятно, какие edge cases важны;
- непонятно, что проверять;
- агент вынужден сам выбирать product behavior.
Лучше:
Неавторизованный пользователь получает HTTP 401 при обращении к /api/profile.
Еще лучше:
Requirement AUTH-004
Given:
пользователь не аутентифицирован
When:
GET /api/profile
Then:
HTTP status = 401
body.error.code = "UNAUTHORIZED"
ответ не содержит пользовательских данных
В последнем варианте уже видно поведение, условие, действие, expected result и проверяемые детали ответа.
Ключевые термины
| Термин | Значение |
|---|---|
| Specification | Документ или раздел, описывающий ожидаемое поведение системы и ограничения. |
| Observable behavior | Поведение, которое можно наблюдать через API, UI, database state, event, log или другой внешний эффект. |
| Requirement | Отдельное требование к поведению, ограничению или качеству системы. |
| Functional requirement | Требование к функции или действию системы. |
| Non-functional requirement | Требование к качеству: performance, security, reliability, usability. |
| Constraint | Ограничение на решение: что нельзя менять, какие технологии использовать, какие boundaries соблюдать. |
| Invariant | Условие, которое должно оставаться истинным всегда. |
| Edge case | Пограничная ситуация, которую легко забыть, но важно определить. |
| Failure scenario | Сценарий ошибки и ожидаемое поведение системы в этой ошибке. |
| Acceptance criteria | Проверяемые условия, по которым задача считается выполненной. |
Что specification делает для агента
Для человека specification - это способ договориться. Для агента это еще и способ ограничить пространство решений.
Без specification агент видит задачу так:
User intent
↓
Many possible interpretations
↓
Agent guesses
↓
Code
Со specification workflow становится другим:
Агенту не нужно угадывать, что означает "хорошо". Он должен реализовать конкретные requirements и пройти verification.
Observable behavior
Главный критерий хорошей specification: можно ли наблюдать и проверить поведение?
Примеры observable behavior:
| Тип поведения | Пример |
|---|---|
| API response | POST /reset-password возвращает 400 TOKEN_EXPIRED для истекшего token. |
| UI state | После успешной отправки формы пользователь видит экран "Проверьте email". |
| Database state | После использования token поле consumed_at заполнено. |
| Event | После смены пароля публикуется событие PasswordResetCompleted. |
| Для существующего пользователя создается email message с reset link. | |
| Audit log | В журнал пишется событие PASSWORD_RESET_REQUESTED. |
Не все observable behavior обязано быть публичным API. Но оно должно иметь способ проверки: тест, лог, database assertion, UI screenshot, event assertion или manual acceptance checklist.
Типы требований
Functional requirements
Описывают, что система делает.
REQ-AUTH-001:
Пользователь может запросить reset password по email.
Non-functional requirements
Описывают качество или ограничение поведения.
REQ-AUTH-009:
Endpoint request reset password должен отвечать менее чем за 500 ms при нормальной нагрузке.
Constraints
Ограничивают способ реализации.
CONSTRAINT-AUTH-001:
Не менять существующий login flow.
Invariants
Должны оставаться истинными всегда.
INV-AUTH-001:
Reset token может быть использован не более одного раза.
Edge cases
Описывают пограничные ситуации.
EDGE-AUTH-001:
Если пользователь запрашивает reset повторно, старый активный token становится недействительным.
Failure scenarios
Описывают ошибки и ожидаемую реакцию системы.
FAIL-AUTH-001:
Если token истек, API возвращает 400 TOKEN_EXPIRED и не меняет пароль.
Acceptance criteria
Requirement отвечает на вопрос "что должно быть верно". Acceptance criterion отвечает на вопрос "по какому наблюдаемому условию мы признаем это выполненным".
Пример:
REQ-AUTH-004:
Неавторизованный пользователь не может получить профиль.
AC-AUTH-004-01:
Given пользователь не аутентифицирован
When GET /api/profile
Then API возвращает HTTP 401
And body.error.code = "UNAUTHORIZED"
And response body не содержит user.email
Acceptance criteria особенно важны для AI-разработки: они становятся мостом между specification и verification. В следующих модулях мы будем связывать их с тестами.
Specification vs prompt
Prompt и specification решают разные задачи.
| Вопрос | Prompt | Specification |
|---|---|---|
| Для чего нужен | Запустить конкретное взаимодействие с агентом | Зафиксировать expected behavior |
| Где живет | В chat/session | В репозитории или source of truth |
| Стабильность | Может быть одноразовым | Должна переживать agent runs |
| Проверяемость | Часто неформальная | Должна содержать AC/constraints |
| Роль агента | Инструкция на текущий шаг | Контракт поведения |
Prompt может ссылаться на specification:
Реализуй TASK AUTH-003 согласно specs/password-reset/spec.md.
Сначала прочитай requirements AUTH-001..AUTH-004 и acceptance criteria.
Не меняй файлы вне src/auth и tests/auth.
Но prompt не должен быть единственным местом, где живет продуктовая логика.
Три уровня зрелости SDD
В обычной разработке specification часто воспринималась как документ "до старта". В AI-assisted development это слабая модель. Агент может быстро менять код, поэтому specification должна не только запускать работу, но и удерживать intent во время изменений.
Удобно различать три уровня зрелости.
| Уровень | Что происходит | Когда достаточно | Риск |
|---|---|---|---|
| Spec-first | Перед кодом пишется spec, затем agent реализует change. | Небольшая feature или учебный проект. | После merge spec может устареть. |
| Spec-anchored | Spec остается якорем для планирования, review, тестов и следующих изменений. | Реальный продукт, несколько features, команда. | Нужно дисциплинированно обновлять spec при изменениях. |
| Spec-as-source | Code, tests или часть artifacts генерируются из spec, а ручные изменения идут через spec. | Узкие домены с формальным contract: API, IaC, workflows. | Нужны строгие tooling и ownership, иначе процесс станет хрупким. |
Для курса базовый целевой уровень - spec-anchored. Мы не пытаемся сразу сделать specification единственным исходником всего кода. Но мы хотим, чтобы при конфликте между prompt, старым comment, догадкой агента и утвержденным spec побеждал spec.
Живой spec - это не waterfall
Распространенное возражение:
Если мы пишем spec до кода, это же waterfall?
Нет. Waterfall ломается, когда документ считается замороженным и оторванным от обратной связи. SDD в agent workflow работает иначе:
изменилось понимание -> обнови spec -> обнови plan/tasks -> меняй code -> проверь evidence
Spec живет рядом с кодом и меняется первым. История изменений spec становится журналом "почему" продукт изменился. Code показывает, что реализовано. Tests показывают, что проверено. Spec объясняет intent, constraints и trade-offs.
Практическое правило:
Если во время implementation агент обнаружил,
что требование неверное или неполное,
он должен вернуться к spec,
а не молча принимать новое product decision в коде.
Для длинных задач полезно разделять документ на две зоны:
| Зона | Кто владеет | Что хранить |
|---|---|---|
| Human-owned spec | Product/tech owner | Goals, requirements, non-goals, acceptance criteria, constraints. |
| Agent working notes | Agent / implementer | Исследования, варианты реализации, временные notes, вопросы. |
Так spec остается читаемым источником истины, а агентные черновики не загрязняют продуктовую часть документа.
Практика: переписать размытое требование
Возьмите фразу:
Сделай хороший authentication.
Перепишите ее в 3-5 requirements.
Пример результата:
REQ-AUTH-001:
Пользователь с валидным email и password получает access token при POST /api/login.
REQ-AUTH-002:
Пользователь с неверным password получает HTTP 401 и error.code = "INVALID_CREDENTIALS".
REQ-AUTH-003:
Неавторизованный пользователь получает HTTP 401 при GET /api/profile.
REQ-AUTH-004:
После logout текущий refresh token становится недействительным.
REQ-AUTH-005:
API login не раскрывает, существует ли email в системе.
Для каждого requirement добавьте хотя бы один acceptance criterion.
Типичные ошибки
Ошибка 1. Писать пожелания вместо поведения
Фразы "удобный", "надежный", "хороший", "быстрый" полезны как направление, но их нужно уточнять до наблюдаемых условий.
Ошибка 2. Прятать constraints в голове
Если нельзя менять API contract или database schema, это должно быть написано явно. Иначе агент может выбрать путь, который кажется ему простым.
Ошибка 3. Не описывать failure scenarios
Большая часть сложностей появляется не на happy path, а на expired token, missing permissions, duplicate request, invalid state и network failure.
Ошибка 4. Смешивать specification и implementation plan
Specification отвечает "что должно быть построено". Implementation plan отвечает "как мы это построим". Если смешать их слишком рано, агент может зафиксироваться на неверном техническом решении.
Ошибка 5. Считать acceptance criteria тестами
Acceptance criteria задают проверяемые условия. Тесты реализуют часть этих проверок. Это разные уровни, хотя они должны быть связаны.
Ошибка 6. Обновлять код, но не обновлять spec
Если behavior изменился только в implementation, следующий agent run снова увидит старую картину мира. Для AI-разработки это особенно опасно: агент будет уверенно опираться на устаревший source of truth.
Исправление:
Behavior change -> spec change -> tests/evidence -> code change.
Иногда порядок внутри маленькой exploratory spike может быть другим, но перед merge итоговый spec должен отражать реальное решение.
Критерии готовности
После урока студент должен уметь:
- объяснить specification как observable behavior;
- отличить пожелание от requirement;
- привести примеры functional и non-functional requirements;
- сформулировать constraint, invariant, edge case и failure scenario;
- написать acceptance criterion в формате Given/When/Then;
- объяснить, почему agent не должен угадывать product behavior.
Краткое резюме
Specification - это не длинное ТЗ ради процесса. Это контракт поведения, который снижает число догадок агента.
Хорошая specification:
- описывает observable behavior;
- фиксирует constraints и edge cases;
- отделяет требования от реализации;
- содержит acceptance criteria;
- живет в source of truth, а не только в chat history.
Для AI Agent Development это фундамент: агент может двигаться быстро, но правильность движения задается specification и проверяется внешними системами.
Вопросы для самопроверки
- Почему "сделай хороший authentication" не является specification?
- Что означает observable behavior?
- Чем functional requirement отличается от constraint?
- Зачем нужны failure scenarios?
- Почему acceptance criteria важны для agent workflow?
Что дальше
В следующем уроке мы разберем, почему user story недостаточно для coding agent. User story хорошо описывает намерение пользователя, но не закрывает edge cases, constraints и проверяемые условия поведения.