Зачем это нужно
Урок 16.8 ввел три модели хранения спецификаций как конвенции. Осталась практика: как именно поддерживать артефакты после внедрения (урок 16.17) - по какому циклу работает каждая модель и как не утонуть в ручном reconcil-е.
Вторая тема урока - контрактный подход: когда компоненты открывают интерфейсы друг другу, договориться об observable-обязательствах нужно до реализации каждой стороны. Спека фичи описывает поведение продукта, контракт - как компоненты сотрудничают для его доставки, план компонента - его внутреннее решение. Гайд CDD показывает, как это делается существующими командами Spec Kit и обычными версионируемыми артефактами - без автоматической синхронизации, cross-repo-резолвера зависимостей и релизной оркестрации.
Основная идея
Существующему проекту нужны два отдельных maintenance-цикла: обновление проектных файлов Spec Kit (managed-команды, скрипты, шаблоны, общие memory-файлы - апгрейд-воркфлоу урока 16.2) и эволюция артефактов фич (репозиторий-специфичные specs/, выровненные с кодом и поведением продукта). Для второго - по циклу на модель хранения. Для интерфейсов между компонентами - контракты с единственным авторитетным владельцем.
Эволюция спек: циклы моделей
Flow-forward: новая фича - новый каталог
Когда добавляется фича или существенное продолжение, создавайте новую спеку через установленный /speckit.specify и идите стандартным потоком:
/speckit.specify- новый каталог фичи подspecs/;/speckit.plan- подход к реализации;/speckit.tasks- разбивка работы;/speckit.implement- ревью код- и артефакт-diff вместе;/speckit.converge- полнота; при дописанных задачах повторять implement/converge до полного завершения.
Прежний каталог фичи остается нетронутым - для аудита, сравнения, объяснения текущего состояния проекта. Используйте ясные имена фич и кросс-ссылки, когда новый каталог сменяет или расширяет раннюю работу.
Living spec: спека - контракт
Когда намеренное поведение меняется, сначала ревизуйте существующий spec.md, затем регенерируйте или вручную правьте производные артефакты:
- Начните с чистого рабочего дерева или выделенной ветки - каждая генерация reviewable;
- Обновите
spec.mdчерез/speckit.clarifyили явной правкой; - Перезапустите
/speckit.planили отревизуйтеplan.mdпод новую спеку; - Перезапустите
/speckit.tasksили отревизуйтеtasks.mdпод новый план; /speckit.analyzeдо возобновления реализации - поймать пробелы между спекой, планом и задачами;/speckit.implement, ревью кода и артефактов вместе;/speckit.converge; при дописанных задачах - цикл до завершения.
Сохраняйте важную обоснованность реализации до замены производных артефактов: если план или список задач содержат решения, которые все еще важны, переносите их явно.
Flow-back: правки оттуда, где всплыло открытие
Первый полезный edit происходит там, где landed находка: spec.md, plan.md, tasks.md или реализация. После изменения приведите набор в соответствие:
- Зафиксируйте открытие в ближайшем к работе артефакте;
- Решите, меняет ли оно намеренное поведение, стратегию реализации, разбивку задач или только код;
- Обновите остальные артефакты, разошедшиеся с принятым направлением;
/speckit.analyze- проверить пробелы междуspec.md,plan.md,tasks.md;- Продолжайте реализацию только после того, как набор описывает поведение и подход, которым будущие контрибьюторы смогут доверять.
Flow-back гибок, но требует дисциплины: не оставляйте нижележащее изменение в tasks.md или коде, если spec.md говорит другое и спека должна оставаться заслуживающей доверия.
До обновления проектных файлов
Перед refresh-ом файлов проекта через specify init --here --force --integration <your-agent> защищайте проект-специфический материал вне specs/ - особенно .specify/memory/constitution.md и кастомизированные файлы в .specify/templates/ и .specify/scripts/. Каталог specs/ не входит в template-пакет, но общие файлы проекта форсированный refresh может перезаписать.
Contract-Driven Development
Когда компонент открывает интерфейс внешнему потребителю, определите контракт, на который потребитель может полагаться. Контрактный подход: договориться о контракте, реализовать каждую сторону против него и проверить, что обе стороны выполняют обязательства.
Место внутри SDD: продуктовые требования фиксируют намеренное поведение; контракты определяют, как компоненты сотрудничают для его доставки; план реализации каждого компонента описывает внутреннее решение. Определяйте границу взаимодействия первой, вместо того чтобы считать границей репозиториев дизайн.
Когда нужен контракт
«Внешний» - за пределами границы реализации компонента, не обязательно за пределами процесса, репозитория или организации. Примеры: сайт зовет backend API; сервис зовет другой или потребляет его события; приложение использует библиотеку или интерфейс плагина; автоматизация вызывает CLI и парсит вывод; компоненты обмениваются файлами в согласованном формате.
Принцип применим к монолитам, модульным приложениям, микросервисам и прочим архитектурам - раскладка репозиториев не определяет, нужен ли контракт. Форма и детальность должны соответствовать интерфейсу: маленькой библиотеке хватит документированных сигнатур и поведения; публичному HTTP API - OpenAPI-схемы и исполняемых примеров. Не каждому контракту нужны отдельная схема или реестр.
Что определяет контракт
Observable-обязательства, позволяющие реализовать каждую сторону, не завися от внутренностей другой:
- принимаемые входы и производимые выходы, включая форматы и правила валидации;
- поведение и побочные эффекты, включая ошибочные случаи;
- релевантные гарантии взаимодействия: идемпотентность, порядок событий, ретраи, таймауты;
- ожидания совместимости, версионирование и политика деприкации;
- примеры и критерии верификации для провайдеров и потребителей.
Схема alone может не покрыть весь договор: форма order-запроса не объясняет, может ли ретрай создать второй заказ. Документируйте и семантику. Не экспонируйте внутренние структуры БД и детали реализации, если они не часть публичного интерфейса.
Один авторитетный владелец
Одна сторона владеет авторитетным контрактом; другая потребляет его. Обычно владелец - компонент, открывающий интерфейс. Делайте владение явным для каждого интерфейса; затронутые потребители участвуют в согласовании изменений - владение не означает односторонних правок.
| Ситуация | Как делиться контрактом |
|---|---|
| Обе стороны в одном репозитории | ссылаться на один авторитетный файл/артефакт вместо раздельных определений; независимо релизящиеся компоненты все равно фиксируют поддерживаемую версию контракта |
| Стороны в разных репозиториях | публиковать версионируемый артефакт/пакет для пиновки потребителями или синхронизировать копию в каждый потребляющий репозиторий |
| Потребитель держит синхронизированную копию | записать авторитетный источник и точную версию/коммит; копия - read-only input; изменения предлагаются в источнике |
Для вендоренной копии соседний README фиксирует репозиторий-источник, путь, неизменяемую ревизию или digest релизного артефакта и процедуру обновления; пакетная зависимость записывает эквивалент в lockfile. Потребителям нужен авторизованный доступ к артефакту контракта - доступ к репозиторию реализации провайдера не обязателен.
Обновления - осознанные: согласовать изменение в авторитетном источнике, опубликовать версию, затем ревьюить обновление версии/копии у каждого потребителя. Не синхронизируйте всех молча на «latest». Запинованный контракт идентифицирует договор, но не гарантирует, что деплойнутый провайдер его все еще поддерживает.
Workflow Spec Kit для контрактов
- Зафиксировать намерение и найти интерфейсы.
/speckit.specifyфиксирует пользовательское поведение; определите, какие компоненты предоставляют и потребляют каждое взаимодействие. Если контракта нет - его определение часть работы, а не пререквизит, который должен был быть где-то готов. - Договориться до зависимой реализации. В
/speckit.planвладелец драфтит контракт и ревьюит с потребителями; вопросы observable-поведения и совместимости решаются до того, как любая сторона реализует предположения о другой. Дальше стороны развиваются независимо против согласованной версии. - Доставить договор каждому затронутому проекту. Дайте агенту точную версию и доступное содержимое контракта плюс релевантные требования. Не предполагайте, что ссылка открывает файлы чужого репозитория. Каждый проект владеет локальными
spec.md/plan.md/tasks.md; ссылайтесь на контракт, не копируйте чужой план. - Спланировать верификацию явно. Попросите
/speckit.tasksвключить контрактные тесты провайдера и потребителя, включая негативные случаи. Тестируйте observable-поведение, не только формы данных. Тесты потребителя по мокам полезны, но не доказывают конформность реального провайдера - включайте верификацию провайдера и интеграционную валидацию. - Реализовать и валидировать каждую сторону.
/speckit.implementв каждом проекте, затем согласованные проверки. Если реализация вскрыла проблему контракта - ревизуйте авторитетный договор и reconcil-ьте затронутые спеки, планы, тесты и версии потребителей, а не правьте копию приватно. - Релизить совместимые реализации. Отслеживайте, какие деплойнутые версии компонентов поддерживают какие версии контрактов; последовательность rollout и rollback - вокруг этих зависимостей. Для ломающих изменений - договоритесь о миграции и периоде перекрытия до удаления поведения, которое потребители еще используют.
/speckit.plan уже производит feature-scoped артефакты contracts/ при внешних интерфейсах. Явно выберите авторитетный артефакт: фича может драфтить новый контракт там или предлагать изменение установленного контракта в другом месте проекта; зафиксируйте одобренный источник и версию в артефактах фичи. Исторические снапшоты и синхронизированные копии - не дополнительные редактируемые источники правды; Spec Kit не публикует и не синхронизирует их автоматически.
Разобранный пример: чекаут сайта через сервисы
Гипотетический чекаут: сайт, сервис заказов и сервис инвентаря, каждый в своем репозитории, контрактов нет. Требование: клиент может оформить заказ, только если все позиции резервируются, а ретрай запроса не должен создавать дубль заказа или резерва.
Сначала - идентифицировать и согласовать два контракта (сводки; авторитетные артефакты обязаны определить и детали входов/выходов):
| Контракт | Владелец/провайдер | Потребитель | Observable-договор |
|---|---|---|---|
| Orders API | Сервис заказов | Сайт | принимает order-запрос с ключом идемпотентности; возвращает заказ или определенную ошибку; ретраи не создают дублей |
| Inventory API | Сервис инвентаря | Сервис заказов | резервирует все позиции или репортит нехватку без частичного резерва; ретраи не создают дублей |
Сервис заказов - провайдер одного интерфейса и потребитель другого: он владеет контрактом Orders API, не Inventory API.
Раскладка на синхронизированных копиях:
inventory-repo/
.specify/
contracts/inventory-api.yaml # авторитетный контракт Inventory API
specs/001-reserve-inventory/
orders-repo/
.specify/
contracts/orders-api.yaml # авторитетный контракт Orders API
contracts/inventory-api.yaml # read-only копия, пин Inventory API 1.0.0
contracts/README.md # ревизия источника и процедура синхронизации
specs/001-place-order/
website-repo/
.specify/
contracts/orders-api.yaml # read-only копия, пин Orders API 1.0.0
contracts/README.md
specs/001-checkout/
Имена и версии иллюстративны, не обязательная раскладка. Каждый проект инициализирован независимо и ведет обычный SDD-цикл:
- Команда инвентаря согласует Inventory API 1.0.0 с командой заказов; команда заказов - Orders API 1.0.0 с сайтом. Каждый владелец публикует одобренный контракт, потребители пинят копии.
- Каждая команда специфицирует и планирует свою часть чекаута против договоров: сайт - взаимодействие клиента, заказы - размещение заказа, инвентарь - резервирование. План заказов учитывает оба контракта и сбои между резервом и созданием заказа.
- Свяжите локальные спеки с одним issue/roadmap-вхождением продуктового сценария; зафиксируйте версии контрактов и связанные спеки/PR сторон. Локальные номера фич и ветки совпадать не обязаны - это трассируемость, не общий план реализации.
- Реализуйте и проверьте оба интерфейса, включая ретраи, нехватку и сбои; валидируйте полный путь чекаута и обязательства каждого компонента.
- Для начального rollout: сделать реализацию инвентаря доступной до включения зависимой реализации заказов, затем включить чекаут сайта. Зафиксируйте деплойнутые версии и ограничения rollback - публикация контракта alone не делает реализацию доступной.
Если Orders API 1.1.0 позже добавляет опциональную заметку о доставке: предложите изменение в orders-repo, проверьте совместимость с потребителями, затем сайт осознанно принимает новую версию. Не редактируйте синхронизированную копию сайта, изобретая поддержку, которой сервис заказов не согласовал.
Тот же дизайн работает и в одном репозитории на три компонента: ссылайтесь на авторитетные контракты напрямую вместо синхронизации копий (раскладка и таргетинг - урок 16.17). Контракт соединяет реализации; раскладка репозиториев меняет только способ шаринга договора.
Пример: control plane, data plane и интеграционные тесты
Три репозитория: control plane (r1) управляет конфигурацией, data plane (r2) применяет ее в трафике, интеграционные тесты (r3) проверяют взаимодействие. Это то же применение принципа, не требование так организовывать системы.
Определить взаимодействие первым - если контракта еще нет, договоритесь об observable-поведении до реализации предположений:
| Аспект контракта | Пример договора |
|---|---|
| Входы | ревизия конфигурации и payload с явными правилами валидации |
| Выходы | acknowledgement с ревизией и статусом (принято/применено); как наблюдать завершение при асинхронности |
| Ошибки | определенные ответы для невалидной конфигурации и устаревшей ревизии; прежняя валидная конфигурация остается активной |
| Поведение | ретрай той же ревизии и payload не применяет ее дважды; переиспользование ревизии с другим payload отвергается |
| Совместимость | идентификация поддерживаемых версий контракта; сохранение поведения, на которое опираются текущие вызовы, на время миграции |
Здесь r2 открывает интерфейс и владеет авторитетным контрактом; r1 участвует в согласовании и потребляет пинованную версию; r3 использует ту же версию для верификации взаимодействия - включая невалидный ввод, ретраи и совместимость. Тесты - подтверждения договора, не конкурирующее его определение. Каждый проект держит собственный план; ссылайтесь на общий продуктовый интент и версию контракта без зонтичного репозитория, совпадающих имен веток и атомарных коммитов.
Эволюция согласованного контракта (rollout-последовательность, адаптирующая гайдлайн контрибуции TongyiDai) - пусть согласовано добавление опционального поля конфигурации:
- Опубликовать совместимое изменение. Обновить авторитетный контракт в
r2, дать явную версию, опубликовать схему и fixtures совместимости; проверить, что отсутствие поля сохраняет прежнее поведение. - Деплой поддержки до включения использования. Если
r1нужно новое поведениеr2- задеплоитьr2в режиме поддержки старых и новых потребителей, только потом включать зависимое поведение вr1. Порядок следует зависимости интерфейса, не именам репозиториев или порядку мержа PR. - Готовить version-aware интеграционные тесты. Добавить покрытие в
r3заранее; выбирать ожидания по поддерживаемой версии контракта или явной capability, не по имени ветки. Существующие проверки совместимости держать обязательными; новый end-to-end гейт сделать обязательным до включения фичи и не пропускать молча после активации. - Трекать готовность неизменяемыми ссылками. Issue/roadmap связывает одобренный контракт, релизы провайдера и потребителей, end-to-end результаты и критерии удаления старого пути с точными коммитами или релизными артефактами; различать смерженное и деплойнутое-готовое.
- Сохранить путь отката. Для provider-first rollout: отключить/откатить новое поведение потребителя раньше отката провайдера; проверить совместимость персистентного состояния и конфигурации - смена порядка деплоя alone не отменяет изменения данных. Старый контракт удалять только поздней очисткой, убедившись, что поддерживаемые потребители больше не зависят от него.
Если ломающее изменение неизбежно: расширить поддержку для миграции, осознанно перевести потребителей и только потом retir-ить старое поведение. Версионированный контракт и запись готовности не превращают отдельные деплои в транзакцию: это командные договоры и проверки, а не новый резолвер зависимостей или релизный оркестратор Spec Kit.
Практика
- Проведите одну реальную итерацию living-модели: измените требование в
spec.mdсвоего проекта, регенерируйте план и задачи, прогоните/speckit.analyzeи добейтесь чистого отчета. - Смоделируйте flow-back: внесите «находку реализации» прямо в
tasks.md, затем по циклу поднимите ее доspec.mdи убедитесь, что/speckit.analyzeне видит рассогласования. - Для любого своего межкомпонентного интерфейса составьте таблицу «аспект-договор» по образцу примера урока и определите авторитетного владельца; проверьте, покрыта ли семантика ретраев и идемпотентности.
Краткое резюме
Эволюция спек - два отдельных цикла: обновление проектных файлов Spec Kit и эволюция артефактов фич по выбранной модели (flow-forward - новая фича в новый каталог; living - спека первой, производные регенерируются, analyze перед реализацией; flow-back - правка там, где открытие, затем дисциплинированный reconcil). Контрактный подход: observable-договор до реализации сторон, один авторитетный владелец интерфейса, пинованные версии у потребителей, верификация провайдера и потребителя в задачах, осознанная эволюция с provider-first порядком и сохранением пути отката.
Главное из урока:
- до
init --here --forceзащищайте конституцию и кастомизации - общие файлы могут перезаписаться,specs/не входит в шаблоны; - схема не заменяет семантику: идемпотентность, ретраи и порядок событий - часть контракта;
- копия контракта у потребителя - read-only; изменения предлагаются в источнике;
- тесты потребителя по мокам не доказывают конформность провайдера;
- publication контракта != доступность реализации: трекайте деплойнутые версии и порядок rollout/rollback.
Вопросы для самопроверки
- Назовите два отдельных maintenance-цикла существующего проекта. Какими командами идет каждый?
- Опишите цикл living-модели: с чего начинается изменение и где стоит
/speckit.analyze? - В чем главный риск flow-back и какая дисциплина его сдерживает?
- Что означает «внешний потребитель» в CDD? Почему раскладка репозиториев не определяет необходимость контракта?
- Как распределяется владение контрактом? Какие три способа шаринга описаны и когда каждый уместен?
- Опишите порядок provider-first rollout при добавлении опционального поля. Почему откат потребителя предшествует откату провайдера?
Что дальше
Инструментальные темы исчерпаны. Урок 16.19 - экосистема сообщества: как публиковать и находить расширения, пресеты, бандлы и walkthroughs, и что такое friends-проекты.