Skip to content

Цикл одной спеки · теория

Spec · Delta · R_eff

Как написать спецификацию, которая переживёт шесть месяцев и три рефакторинга. Spec отвечает на «как система должна себя вести» - поведение, которое можно протестировать. Delta + DAG сохраняют историю требований и связи между спеками. R_eff с правилом WLNK честно показывает, насколько техническое решение внутри спеки надёжно, и через Evidence Decay сигнализирует, когда пора пересматривать.

01 · Spec
Проверяемое поведение
Поведение · Invariants · Триггеры · Out-of-scope
02 · Delta + DAG
git diff для требований
ADDED · MODIFIED · REMOVED + граф зависимостей
03 · R_eff
trust = min(scores)
Verdict · CL · Evidence Decay

01 · Spec против PRD - разница в способе проверки

Концепция 01 · аналогия рецепта и контрольного вкуса

PRD говорит «что делать». Spec говорит «как система должна себя вести»

Разница маленькая на вид, огромная по последствиям. PRD отвечает на вопрос «что делать». Spec отвечает на вопрос «как система должна себя вести».

Аналогия - рецепт против контрольного вкуса. Рецепт может звучать вкусно и быть при этом совершенно невыполнимым; финальный вкус блюда проверяется только едой. PRD - рецепт. Spec - контрольный вкус.

PRD · намерение
«Добавить теги к заметкам»
Благозвучно, на уровне продукта. Понятно, чего хочет пользователь. Но: тест не напишешь, прогнать нельзя, «соответствует / не соответствует» неотличимо.
не проверяемо · level: продукт
Spec · поведение
«POST /notes/:id/tags с массивом из 1-10 ASCII-строк → 200 + список тегов; дубликаты идемпотентны; пустой массив → 400»
Проверяемое поведение. Можно написать тест, прогнать, отличить «соответствует» от «не соответствует». Контракт между ожиданием и реализацией.
проверяемо · level: поведение системы
notes-tagging-spec.md · минимальный скелет active
Поведение
  • POST /notes/:id/tags с валидным массивом → 200 + список тегов
  • POST с дубликатом существующего тега → 200, идемпотентно
  • POST с пустым массивом → 400 {"error": "empty_tags"}
Invariants
  • Тег: 1-32 ASCII символа, нормализация - lowercase
  • Максимум 10 тегов на заметку
Триггеры · review zone
  • Изменение лимита тегов → review search-spec (фильтрация)
  • Смена нормализации → review migration-spec
Out-of-scope
  • Иерархия тегов (покрывается tag-hierarchy-spec в будущем)
  • Цвета и иконки тегов
⚡ обязательная секция
Out-of-scope - это то, что делает specs composable

В PRD эту секцию часто пропускают, в spec - никогда. Одна спека отвечает за одну границу поведения, всё остальное - соседние спеки. Вместо одного монолита у вас набор маленьких независимых файлов, каждый проверяется и меняется отдельно.

02 · Delta-spec и DAG зависимостей

Концепция 02 · аналогия git diff для требований

Спека не переписывается - она меняется через delta

Допустим, у вас уже есть notes-spec.md. Теперь вы добавляете теги. Плохой путь - переписать всю спеку в v2 и закоммитить поверх. Через три месяца никто не поймёт, что именно изменилось. Хороший путь - написать delta-spec: явный diff с тремя секциями.

+ ADDED
Что появилось
Новые requirements, которых не было. Например - POST /notes/:id/tags с массивом 1-10 строк, дубликаты идемпотентны.
~ MODIFIED
Что изменилось
Существующие requirements с пометкой BEFORE / AFTER. Например - GET /notes получил опциональный параметр ?tag=<name> для фильтрации.
− REMOVED
Что убрали
Requirements, которые перестают действовать. В этой фиче пусто - но если бы был старый эндпоинт /tag-search, он бы попал сюда с причиной.
delta-notes-tagging.md · пример # Delta for Notes - add tagging ## ADDED Requirements ### Requirement: Notes can be tagged POST /notes/:id/tags принимает массив 1-10 строк (1-32 ASCII каждая). Дубликаты идемпотентны. Возвращает 200 + полный список тегов. ## MODIFIED Requirements ### Requirement: Notes listing BEFORE: GET /notes возвращает все заметки пользователя. AFTER: GET /notes принимает опциональный ?tag=<name> для фильтрации. Без ?tag - поведение прежнее. ## REMOVED Requirements (пусто - в этой фиче ничего не удаляется)
Каскад правок · что ещё придётся открыть, когда меняется notes-spec
source notes-spec modified
search-spec
review · direct
85%
логика фильтрации
migration-spec
review · direct
80%
схема БД
api-spec
new endpoint
60%
новый endpoint
search-spec → api-spec
transitive
25%
может всплыть позже
прямая · обязательный REVIEW аддитивная · NEW ENDPOINT транзитивная · мониторим

Каскад явно записывается в секции Триггеры или в отдельном файле зависимостей. Когда вы готовите delta, чарт показывает, что ещё нужно проверить - это уберегает от ситуации «поменяли одну спеку, через месяц всплыло в трёх соседних». Длина бара - грубая оценка усилий ревью, не точное число.

Delta - контракт между прошлым и будущим

Без delta каждая правка спеки стирает историю. С ним вы можете через 6 месяцев открыть git log openspec/changes/ и пройти по каждой ADDED / MODIFIED / REMOVED Requirement в обратном порядке - как git blame для требований. Это не формальность, это страховка от «почему у нас в GET /notes появился параметр ?tag».

03 · R_eff - координаты качества + Evidence Decay

Концепция 03 · аналогия техосмотра

«Годен» - это не среднее, а способность определить опасную систему

Аналогия - техосмотр автомобиля. Инспектор не ставит одну общую оценку «машина хорошая». Он проверяет несколько независимых систем: тормоза, подвеска, движок, электрика, кузов. Общий вердикт «годен / не годен» - это не среднее, а способность ответить: «есть ли хоть одна система, которая сделает поездку опасной». Если тормоза 2 из 9 - какие бы хорошие ни были подвеска и движок, машина на дорогу не идёт. Через 6 месяцев - повторный техосмотр, старый результат недействителен.

R_eff работает ровно так же. Каждое evidence (доказательство, которое поддерживает или опровергает решение) имеет два измерения - Verdict и CL - и итоговый score, по которому считается общий R_eff = min(scores).

  • Verdict · отношение evidence к решению. supports = 1.0 / weakens = 0.5 / refutes = 0.0.
  • CL · Congruence Level · откуда взято. CL3 ваш замер на этом же проекте (penalty 0.0) · CL2 похожий проект или коллега (penalty 0.1) · CL1 документация / Stack Overflow / блог (penalty 0.4) · CL0 статья про противоположный контекст (penalty 0.9).
  • Оценка · max(0, verdict − CL_penalty) для одного evidence.
  • R_eff · min(scores) по всем evidence решения. Не среднее.
DEC-018 · junction table note_tags vs JSON-колонка notes.tags at risk
# Evidence Verdict CL Оценка
E1 Свой замер на 1000 заметок: JOIN по note_tags(tag_id) p95 < 5 ms; LIKE '%ddd%' p95 ~50 ms supports CL3 1.0
E2 SQLite docs: junction table рекомендован для many-to-many supports CL1 0.6
E3 Рассылка: деградация JSON1 на 100k записей weakens CL1 0.1 ← WLNK
R_eff = min(scores)0.1
Среднее0.57
Вердиктat risk · WLNK = E3
Если считать среднее
0.57
(1.0 + 0.6 + 0.1) / 3
выглядит «нормально» · обманчиво
R_eff = min(scores)
0.1
min(1.0, 0.6, 0.1)
at risk · решение держится на ниточке

Среднее обманывает, минимум - нет. Одно опровергающее evidence (даже слабое, CL1) утаскивает доверие вниз. Это и есть смысл формулы «trust = weakest link».

Evidence Decay · срок годности доказательства
сейчас · fresh
valid_until · 2026-10-01
истекло · stale · оценка → 0.1
Любое evidence имеет срок годности. Бенчмарк 2023 года для Redis 6.x не валиден для Redis 7.4. Наблюдение на продакшене при 50 RPS не применимо к 500 RPS. Хороший артефакт это признаёт заранее через frontmatter valid_until: 2026-10-01 и refresh_trigger: notes_count > 100_000. Когда valid_until истекает - оценка evidence падает до 0.1 (stale), не absent. Решение было, контекст мог измениться, требуется reaffirm или supersede.
R_eff не приговор · это датчик

R_eff = 0.1 не значит «всё плохо, разваливай». Это сигнал «решение держится на одном слабом звене - обратите внимание». Стратегия: либо собрать ещё evidence (CL3-замер, который опровергнет E3 или подтвердит, что ситуация из рассылки не наша), либо принять риск явно с пометкой в DDR «опираемся на E1, E3 слабее, пересмотр через 30 дней или после 100k заметок».

Связано · Trust Calculus в 3D F/G/R-разбор показывает, как WLNK работает на трёх независимых осях evidence - параллельный взгляд на ту же идею «среднее обманывает»

Цикл на одной спеке · Vault · добавление тегов

Как все три концепции складываются в один проход - от пустого файла до решения с честным R_eff.

01 · Spec

Пишете notes-tagging-spec.md с четырьмя секциями

Поведение (3 контракта поведения), Invariants (ASCII 1-32, max 10 тегов), Триггеры (ссылки на search и migration), Out-of-scope (иерархия, цвета). Spec проверяема - каждое поведение можно превратить в тест.

02 · Delta

Пишете delta-notes-tagging.md поверх notes-spec

ADDED: новый endpoint /notes/:id/tags. MODIFIED: GET /notes с опциональным ?tag. REMOVED: пусто. DAG показывает: search-spec и migration-spec требуют review. api-spec добавляет новый endpoint.

03 · R_eff

Считаете для решения «junction table vs JSON»

Три evidence: свой бенчмарк (CL3, supports, 1.0), SQLite docs (CL1, supports, 0.6), рассылка про деградацию JSON1 (CL1, weakens, 0.1). R_eff = min = 0.1. Среднее 0.57 обмануло бы - решение at risk.

04 · Decay

Frontmatter с valid_until и refresh_trigger

В спеку добавляется valid_until: 2026-10-01 и refresh_trigger: notes_count > 100_000. Решение либо подкрепляется новым evidence (CL3-замер на 100k), либо пересматривается. Через 6 месяцев цикл стартует с шага 02 - delta с MODIFIED.