Написание качественных спецификаций
Вы редко пишете спецификацию с чистого листа. Вы описываете изменения на обычном языке, команда /opsx:propose черновик требований и сценариев, а затем вы делаете их хорошими. Эта страница посвящена последней части — тому, как выглядит «хорошо», и как направить ИИ в этом направлении.
Это дополнение к статье Обзор изменений: обзор помогает выявить слабые места в черновике, а написание требует понимания того, из чего состоит качественный документ.
Спецификация описывает поведение, а не код
Спецификация говорит о том, что делает ваша система, в терминах, которые может проверить любой человек, — а не о том, как она устроена. Она состоит из требований (утверждений о поведении) и сценариев (конкретных примеров, подтверждающих эти требования).
### Требование: Тайм-аут сессии
Система ДОЛЖНА завершать сессию после 30 минут бездействия.
#### Сценарий: Простой тайм-аут
- ДАНО аутентифицированная сессия
- КОГДА проходит 30 минут без активности
- ТО сессия становится недействительной, и пользователь должен пройти повторную аутентификациюДетали реализации — очередь, библиотека, схема таблицы — храните в design.md или коде. Когда поведение и реализация смешиваются в одном требовании, оно перестает быть проверяемым и начинает устаревать в момент изменения кода.
Что делает требование хорошим
Хорошее требование описывает одно поведение так просто и понятно, что его можно передать другому человеку для тестирования.
Одно утверждение, один модальный глагол
SHALL/MUST. Если требование содержит три союза «и также», это фактически три разных требования. Разбейте их.Наблюдаемость. Кто-то вне команды разработки должен иметь возможность определить, выполняется ли оно. «Система ДОЛЖНА показывать баннер об ошибке, когда размер загрузки превышает 10 МБ» — это наблюдаемо. «Система ДОЛЖНА корректно обрабатывать большие загрузки» — нет.
Правильная сила утверждения. OpenSpec использует ключевые слова RFC 2119, и они имеют разное значение:
Ключевое слово Значение MUST/SHALLЖесткое требование. Не подлежит обсуждению. SHOULDСтрогая рекомендация, допускающая обоснованное исключение. MAYДействительно опционально. По умолчанию используйте
MUST/SHALL. ИспользуйтеSHOULDтолько тогда, когда вы действительно имеете в виду «если нет веской причины этого не делать».
Тест для требования: смог бы тестировщик, который никогда не видел код, сказать, прошел ли он? Если нет, требование нужно уточнить.
Что делает сценарий хорошим
Сценарии — это то место, где требование доказывает свою ценность. Каждый из них представляет собой конкретный GIVEN / WHEN / THEN, который может стать автоматизированным тестом.
- Он проверяет свое требование. Сценарий, который просто перефразирует требование другими словами, ничего не тестирует. Сделайте его конкретной ситуацией с конкретным результатом.
- Покрытие важных случаев, а не только «счастливого пути» (happy path). Успешный вход в систему прост. Пустой ввод, истекший токен, двойной клик, то, что идет не так — именно здесь живут баги, и именно здесь сценарий приносит наибольшую пользу.
- Название случая в заголовке. «Сценарий: Отклонение истекшего токена» сразу дает рецензенту понять, что проверяется; «Сценарий: Тест 2» — нет.
Полезная привычка: перед утверждением спросите себя: какой один случай я был бы расстроен видеть сломанным? — и убедитесь, что есть сценарий, который его покрывает.
Выберите правильный тип дельты
Изменение описывает свои правки в спецификациях тремя типами разделов. Использование правильного типа сохраняет честность архивных спецификаций:
## ADDED Requirements— совершенно новое поведение, которого раньше не было.## MODIFIED Requirements— существующее поведение, которое изменяется. Включите полную новую версию; краткая заметка о том, что изменилось, поможет рецензенту.## REMOVED Requirements— поведение, которое убирается, с указанием причины.
При архивации добавленные (ADDED) требования добавляются в основную спецификацию, измененные (MODIFIED) заменяют старую версию, а удаленные (REMOVED) исключаются из нее. Удалите последнее требование возможности (capability), и вы ее деактивируете: вместо того чтобы оставлять пустую спецификацию, архив удаляет файл openspec/specs/<capability>/spec.md. Поскольку это единственный шаг архивации, удаляющий файл, он должен выполняться явно — добавьте retire_capabilities: true в .openspec.yaml изменения, рядом с уже необходимым там полем schema:. Без этого архив прервется и сообщит вам об этом. Деактивация удаляет весь файл целиком, поэтому она также отклоняется, если спецификация содержит что-либо помимо заголовка, раздела ## Purpose и блоков требований — например, раздел ## Notes или комментарий под требованием. При прерывании указываются номера этих строк; переместите их в ## Purpose или в требование, либо удалите спецификацию вручную. Для спецификации в рабочей копии вызывающего проекта вывод архива также указывает команду git checkout, восстанавливающую зафиксированный файл; выбранные хранилища получают рекомендации по восстановлению, ограниченные областью проверки. Если вы пометите реальное изменение как ADDED, у вас получится два конкурирующих требования; если вы опишете новое поведение как MODIFIED, нечего будет заменять. Если сомневаетесь, откройте текущую спецификацию и посмотрите, есть ли там уже это требование.
Еще один раздел стоит знать. Когда ваша дельта создает возможность (capability), которой еще не существует, откройте её разделом ## Purpose — предложением или двумя о том, для чего нужна эта возможность. Архив использует его как цель создаваемой им основной спецификации; пропустите его, и вы получите заполняемый вручную плейсхолдер TBD. У существующей спецификации уже есть Цель, поэтому цель дельты там игнорируется — отредактируйте openspec/specs/<capability-path>/spec.md напрямую, чтобы изменить её. Здесь <capability-path> — это путь к директории относительно specs/, например user-auth в плоском проекте или identity/user-auth в проекте, организованном по доменам.
Правильно масштабируйте изменение
Самая распространенная ошибка авторов — не плохо сформулированное требование, а изменение, пытающееся быть тремя разными изменениями.
Хорошее изменение имеет одну цель, которую можно выразить одним предложением. «Добавить переключатель темной темы.» «Ограничить частоту запросов для конечной точки входа.» «Перенести сессии с куки.» Если описание изменения требует много союзов «и также», это сигнал разделить его.
Признаки того, что изменение слишком большое:
- Область предложения читается как список несвязанных функций.
- Его рецензирование заняло бы целый день, поэтому никто этого не сделает.
- Два человека не могут работать над ним без конфликтов.
- Половина задач может быть выпущена самостоятельно.
Меньшие изменения легче рецензировать, проще реализовать за одну сфокусированную сессию и легче анализировать через шесть месяцев, когда архив останется единственным источником истины. Вы всегда можете запустить несколько изменений параллельно — см. Редактирование и итерации и Рабочие процессы.
Происходит и обратное: исправление опечатки в одну строку не требует трех требований и документа дизайна. Соответствуйте церемонии уровню важности.
Как направить ИИ к хорошему черновику
Поскольку /opsx:propose делает первый черновик, качество полученного результата зависит от качества ваших исходных данных. Вам не нужно писать требования вручную — нужно правильно направить ИИ:
- Укажите намерение и границы. «Добавить переключатель темной темы, который при первой загрузке следует настройкам ОС — не затрагивать существующий API тем.» Часть вне области действия так же важна, как и часть внутри неё.
- Назовите случаи, которые вас интересуют. «Убедитесь, что есть сценарий для пользователя, который уже выбрал тему вручную.» ИИ покрывает то, на что вы указываете.
- Затем отредактируйте. Это обычный Markdown. Уточните расплывчатый
SHALL, удалите сценарий, который ничего не тестирует, добавьте упущенный случай — или попросите ИИ сделать это: «требование о тайм-ауте расплывчато, зафиксируйте его на 30 минутах.»
Черновик, уточнение, повтор. Несколько таких циклов создают спецификацию, которой можно доверять, в чем и заключается вся суть.
Быстрый чек-лист
Куда двигаться дальше
- Обзор изменений — двухминутная проверка, которая выявляет проскочившие ошибки.
- Концепции — более глубокая модель спецификаций, изменений и дельт.
- Примеры и рецепты — реальные изменения от начала до конца.