Agent Dev 101

Модуль 15 · Урок 15.11

Урок 15.11. OpenSpec в существующем проекте (brownfield)

На этой странице
  1. Зачем это нужно
  2. Основная идея
  3. Версия на тридцать секунд
  4. Почему delta-first - весь секрет
  5. Первое изменение на реальном codebase
  6. Экскурс с гидом: /opsx:onboard
  7. «У нас уже есть документация требований»
  8. Организация спецификаций в большом codebase
  9. Монорепозитории и работа между репозиториями
  10. Начальный контекст: config.yaml вместо описания всего проекта
  11. Несколько честных предостережений
  12. Карта внедрения: куда идти за деталями
  13. Практика
  14. Краткое резюме
  15. Вопросы для самопроверки
  16. Что дальше
  17. Источники

Зачем это нужно

Урок 2.10 представлял OpenSpec как lightweight SDD-инструмент, который «особенно хорошо подходит для brownfield». Уроки 15.2-15.10 разобрали установку, концепции, команды и качество спецификаций - но на компактных примерах, где проект умещается в пару экранов кода.

Реальность почти всегда другая: проект существует годы, в нем десятки и сотни тысяч строк, документация частичная или устаревшая, а команда не может остановить разработку на месяц ради «сначала опишем всё». Отсюда главный страх любого внедрения: «Мне придется написать спецификации для всего кода, прежде чем OpenSpec станет полезен?»

Этот урок разбивает официальный ответ документации на этот страх. Коротко: нет, не придется. OpenSpec построен brownfield-first, и его модель внедрения рассчитана именно на большой существующий codebase: спецификации пишутся только под то, что вы собираетесь менять, и накапливаются со временем вокруг реальной работы.

Этот урок открывает блок «Внедрение» (15.11-15.13): сначала адаптация на существующем проекте, затем командная работа и Stores, затем кастомизация под свои конвенции. Материал опирается на цикл изменений из урока 15.2, концепцию дельт из урока 15.3 и паттерны подбора команд из урока 15.8 - если какие-то шаги ниже кажутся незнакомыми, вернитесь к этим трем.

Основная идея

Чтобы начать с OpenSpec, не нужно документировать весь codebase. Спецификации пишутся только под то изменение, которое вы делаете прямо сейчас. Это самое важное, что стоит знать о внедрении OpenSpec в существующий проект.

Спецификации наращиваются по одному изменению за раз. Первое изменение документирует срез системы, которого оно касается; следующее изменение - свой срез; через месяцы openspec/specs/ заполняется естественно, вокруг работы, которую вы и так выполняете. Живая спецификация (living spec) здесь не стартовый проект, а побочный продукт нормальной разработки:

flowchart LR C1["Изменение 1: add-rate-limiting"] --> AR["Архивация: дельта вливается в specs"] C2["Изменение 2: add-guest-checkout"] --> AR C3["Изменение 3: fix-token-refresh"] --> AR C4["Изменение N"] --> AR AR --> SP["openspec/specs/ - из почти пустого в актуальную спецификацию"]

Руководство, которое переводится в этом уроке, формулирует это так: «This guide shows how to start on day one without boiling the ocean» - как начать в первый же день, не пытаясь «переварить океан».

Версия на тридцать секунд

Полное внедрение на существующем проекте умещается в две команды терминала и четыре слэш-команды. В терминале:

bash
$ cd your-existing-project
$ openspec init          # создает openspec/ и файлы команд вашего AI-инструмента

Затем в чате coding-агента:

text
/opsx:explore            # необязательно: агент читает область, которую вы затронете
/opsx:propose <настоящее небольшое изменение, которое вам реально нужно>
/opsx:apply
/opsx:archive

Все. После архивации ваши спецификации описывают ровно ту часть системы, которой касалось изменение, - и ничего больше. Это правильный результат. Про остальные 80 000 строк можно больше не думать.

Построчно, что здесь происходит:

  • cd your-existing-project - вы находитесь в корне существующего репозитория; никакого отдельного «проекта для OpenSpec» создавать не нужно;
  • openspec init - создает каталог openspec/ и файлы команд (skills) для вашего AI-инструмента, как в уроке 15.2;
  • /opsx:explore - необязательный, но полезный шаг: агент читает область, которую вы затронете, до каких-либо предложений;
  • /opsx:propose - берет настоящее небольшое изменение, которое вам реально нужно (не выдуманный пример);
  • /opsx:apply, /opsx:archive - реализация и архивация, после чего дельта изменения вливается в openspec/specs/.

Обратите внимание: это тот же самый цикл, что и в уроке 15.2. Разница только в точке старта: не чистый лист, а середина большого существующего проекта - и это штатный режим работы OpenSpec, а не компромисс.

Почему delta-first - весь секрет

Изменения в OpenSpec записываются как дельты (delta specs): операции ADDED, MODIFIED, REMOVED. Дельта описывает, что меняется относительно текущего поведения системы, а не систему целиком. Механика дельт подробно разобрана в уроке 15.3; здесь важна именно ее связь с brownfield.

Работа над существующим проектом редко строится с нуля. Вы добавляете поле, чините редирект, ужесточаете таймаут. Дельта позволяет специфицировать одно такое изменение точно - не написав сначала сорокастраничную спецификацию всего, что его окружает. Формат дельты (требования и сценарии остаются на английском - это часть формата):

markdown
## ADDED Requirements

### Requirement: API rate limiting
The public API SHALL limit requests per client within a time window.

#### Scenario: Client exceeds the limit
- **WHEN** a client sends more requests than the window allows
- **THEN** the API responds with 429 and does not process the excess requests

Следствие для внедрения: каталог openspec/specs/ не стартует полным и завершенным. Он стартует почти пустым и накапливается. Каждое архивированное изменение вливает в него свою дельту:

flowchart LR M1["Изменение: tighten-auth-timeout"] -->|"архивация"| A1["specs/auth/ - первое требование"] M2["Изменение: add-token-rotation"] -->|"архивация"| A2["specs/auth/ - второе требование"] M3["Изменение: fix-token-refresh"] -->|"архивация"| A3["specs/auth/ - обстоятельная спецификация"]

Спецификация домена auth/ становится обстоятельной только после того, как вы сделали несколько auth-изменений, - то есть ровно тогда, когда она и должна стать обстоятельной. Не раньше.

Как это выглядит месяц за месяцем

Развернем ту же логику во времени - типичная картина инкрементального внедрения на живом проекте:

text
Месяц 1:  openspec/specs/
            api/
              spec.md            ← единственный домен: первое изменение
                                     (add-api-rate-limiting)

Месяц 2:  openspec/specs/
            api/
              spec.md            ← второе api-изменение дополнило spec.md
            checkout/
              spec.md            ← новый домен: add-guest-checkout

Месяц 6:  openspec/specs/
            api/                 ← обстоятельная спецификация:
              spec.md              выросла из всех api-изменений полугода
            checkout/
            auth/
            search/

Ни на одном из этих этапов не было шага «описать систему целиком». Каждый spec.md появился из архивированных изменений - и описывает только те части, которые команда реально трогала. Остальные 80 000 строк остались за скобками не потому, что про них «забыли», а потому, что спецификация, которую ничто не обновляет, бесполезна.

Здесь же живет ответ на вопрос «когда спецификация домена становится полной?»: никогда заранее. Полной считается та, которая покрывает изменяемое поведение. Как только вы заходите в непокрытую область - /opsx:explore читает код, /opsx:propose пишет дельту, архивация дополняет spec.md.

Инженерный вывод курса: delta-first снимает «проблему холодного старта», на которой спотыкаются тяжеловесные SDD-процессы. «Сначала опишем всё» - это состояние, которое никогда не наступает: работа мешает, описание устаревает раньше завершения. Перенос написания спецификаций в момент реального изменения означает, что спецификация пишется, когда есть и мотивация, и свежее знание кода, - поэтому она не успевает протухнуть.

Первое изменение на реальном codebase

Выберите что-то маленькое и настоящее. Не игрушечный пример, не переписывание компонента. Изменение, которое вы и так собирались сделать на этой неделе. Небольшие первые изменения позволяют изучить workflow с низкими ставками: если что-то пойдет не так, цена урока минимальна.

Весь путь первого изменения на существующем проекте:

flowchart LR P["Выбор: настоящее,<br/>маленькое изменение"] --> E["/opsx:explore:<br/>агент читает область"] E --> PR["/opsx:propose:<br/>дельта под это изменение"] PR --> AP["/opsx:apply + /opsx:archive:<br/>дельта вливается в specs"] AP --> R["openspec/specs/ описывает<br/>ровно затронутый срез"]

Пунктирной разницы между «проектом для обучения» и «рабочим проектом» здесь нет: учебным полигоном становится сама работа, потому что спецификации пишутся только под нее.

Шаг 1. Дайте агенту прочитать затронутую область

Именно здесь /opsx:explore отрабатывает на большом или незнакомом codebase. Направьте агента на часть системы, которую собираетесь менять, и пусть он составит карту, как все устроено, - до каких-либо предложений:

text
Вы:  /opsx:explore

AI:   Что хотите исследовать?

Вы:   Нужно добавить rate limiting в публичный API, но я не уверен,
      как запросы сейчас проходят через middleware.

AI:   Прослежу путь... [читает router, стек middleware и конфигурацию]
      Запросы попадают в Express, проходят auth-middleware, затем
      в контроллеры. Слоя rate limiting сейчас нет. Самая чистая
      точка вставки - middleware сразу после auth. Составить скоуп?

Ключевой момент: к моменту proposal агент уже понимает вашу фактическую структуру, поэтому предложение будет подогнано под ваш код, а не под generic-шаблон. По оценке документации, на большом codebase именно эта одна привычка экономит больше всего боли. Отдельно команде /opsx:explore посвящен урок 15.7.

Шаг 2. Предложите изменение

text
You: /opsx:propose add-api-rate-limiting

Proposal и его дельта-спецификация захватывают только это изменение - rate limiting, а не «весь API раз и навсегда».

Шаг 3. Соберите и заархивируйте

/opsx:apply и /opsx:archive работают так же, как для любого другого изменения. После архивации у вас есть настоящая спецификация поведения rate limiting, рожденная изменением, которое было нужно и так.

Что останется в репозитории после первого изменения

После архивации первого изменения дерево openspec/ выглядит примерно так (структура артефактов подробно разобрана в уроке 15.2):

text
openspec/
  specs/
    api/
      spec.md        # living spec: поведение rate limiting «как есть»
  changes/
    archive/
      add-api-rate-limiting/
        proposal.md  # зачем делали и что менялось
        specs/
          api/
            spec.md  # дельта, влитая в living spec при архивации
        tasks.md     # чек-лист реализации

Существенно: openspec/specs/api/spec.md описывает только rate limiting. Про аутентификацию, версионирование и остальной публичный API там строк нет - они появятся, только если вы однажды придете менять эти части. А openspec/changes/archive/add-api-rate-limiting/ навсегда сохраняет историю: почему изменение сделали и какой именно дельтой оно вошло в спецификацию.

Инженерный вывод курса: практический критерий выбора первого изменения - три вопроса. Нужно ли оно на этой неделе? Затрагивает ли одну область? Можно ли объяснить его суть в двух предложениях? Если выбранное «изменение» формулируется как «переписать X на Y» - это не первое изменение, а программа на квартал; отложите его и возьмите настоящую маленькую задачу из бэклога.

Экскурс с гидом: /opsx:onboard

Если хочется не делать шаги вручную, а посмотреть весь цикл на собственном коде с комментариями по ходу, есть расширенная команда /opsx:onboard. Она сканирует codebase в поисках небольшого безопасного улучшения, затем проводит вас через предложение, реализацию и архивацию, объясняя каждый шаг.

Сначала включите набор расширенных команд (expanded profile):

bash
$ openspec config profile      # выбор расширенных workflow
$ openspec update              # применить их к этому проекту

Затем в чате:

text
/opsx:onboard

Документация называет это самым мягким способом познакомиться с OpenSpec на реальном проекте. После экскурсии у вас остается настоящее (маленькое) изменение, которое можно оставить или отбросить. Справочник команды /opsx:onboard - в уроке 15.5.

Чем /opsx:onboard отличается от /opsx:explore при первом знакомстве:

/opsx:explore /opsx:onboard
Кто выбирает, что менять вы: указываете интересующую область команда: сканирует codebase и сама находит небольшое безопасное улучшение
Что происходит агент читает код и помогает продумать изменение без обязательств агент проводит полный цикл: предложение, реализация, архивация - с объяснением каждого шага
Результат понимание области и готовый к /opsx:propose скоуп настоящее маленькое изменение, которое можно оставить или отбросить
Нужен expanded profile нет да: openspec config profile + openspec update

Если вы уже знаете, какое изменение делать первым, идите путем из предыдущего раздела. Если хочется сначала посмотреть со стороны, как весь цикл проходит по вашему коду, - начните с /opsx:onboard.

«У нас уже есть документация требований»

Типичное возражение команды с историей: «У нас есть PRD / SRS / формальная спецификация / даже TLA+-модели. Что с ними делать?» Ответ документации: хорошо, что есть. Их не импортируют в OpenSpec целиком - и не выбрасывают.

Существующие документы рассматриваются как исходный материал для exploration, а не как спецификации на конвертацию. Начав изменение, вставьте в чат или укажите агенту релевантный раздел документа - и пусть он сформирует из него сфокусированную дельту OpenSpec. Дельта захватывает то поведение, которое меняется сейчас, в тестируемой форме требований и сценариев. Исходные документы остаются там, где лежали, - в качестве фона:

text
Вы:  /opsx:explore
Вы:   Вот раздел нашего PRD про checkout. Дальше я реализую
      требование «guest checkout».
      [вставляет релевантное требование]
AI:   [читает его, задает уточняющие вопросы, помогает определить скоуп изменения]
Вы:  /opsx:propose add-guest-checkout

Честная причина, почему не работает массовая конвертация: спецификации OpenSpec сознательно behavior-first и скоупированы под изменения. Сорокастраничный PRD - другой артефакт с другой задачей. Одноразовая bulk-конвертация такой документации обычно производит большую неактуальную спецификацию, которой никто не верит. Спецификации, выросшие из реальных изменений, остаются точными - потому что каждое изменение их обновляет.

Инженерный вывод курса: существующая документация требований в этой модели играет ту же роль, что и устное знание кода, - вход для /opsx:explore, а не выход процесса. Ценность PRD не обесценивается: он остается фоном и источником. Но единственный путь информации в openspec/specs/ - через дельту реального изменения.

Организация спецификаций в большом codebase

Спецификации живут в openspec/specs/ и группируются по доменам (domain) - логическим областям, которые соответствуют тому, как ваша команда думает о системе. Возможность (capability) и домен употребляются как синонимы - эта терминология введена в уроке 15.3.

Проектировать всю таксономию доменов заранее не нужно. Папка домена создается тогда, когда первое изменение в этой области в ней нуждается. Типовые способы нарезки:

Способ нарезки Примеры доменов
По функциональной области auth/, payments/, search/
По компоненту api/, frontend/, workers/
По bounded context ordering/, fulfillment/, inventory/

Правило выбора одно: берите то, от чего новичок в проекте кивнет («да, про это payments/»). Уточнить нарезку можно позже - структура каталогов не догма.

Как это выглядит после первых изменений:

text
openspec/specs/
  auth/            # появился после первого auth-изменения
    spec.md
  api/             # появился, когда дошло дело до rate limiting
    spec.md

Два домена - уже работающая структура. Остальные появятся, когда их коснутся реальные изменения.

Инженерный вывод курса: инкрементальное появление доменов - удобный индикатор того, что внедрение идет правильно. Если вы обнаружили себя за проектированием «идеальной таксономии доменов» до первого изменения - остановитесь: это то же самое «boiling the ocean», только перенесенное из спецификаций в структуру каталогов. Правильный порядок обратный: изменение → домен → уточнение нарезки по мере роста.

Монорепозитории и работа между репозиториями

Для монорепозитория простейшая и рекомендуемая модель - один каталог openspec/ в корне репозитория, а домены соответствуют вашим пакетам или сервисам. Этого хватает большинству команд:

text
monorepo/
  openspec/          # один каталог на весь репозиторий
    specs/
      api/
      frontend/
      workers/
  packages/
  services/

Если работа действительно расползается на несколько репозиториев (или несколько пакетов, которые вы считаете отдельными), у OpenSpec есть бета-функция Stores: планирование живет в собственном отдельном репозитории, на который может ссылаться любой из ваших кодовых репозиториев, - план не обязан лежать в openspec/ одного из них. Это beta: команды и формат состояния могут меняться. Ментальная модель и минимальный путь разобраны в уроке 15.12.

Начальный контекст: config.yaml вместо описания всего проекта

Осталось закрыть вопрос, который часто возникает после урока 2.10: где при brownfield-старте живет «начальный контекст проекта» - короткое описание стека и конвенций, которое агент читает перед каждым предложением?

В срезе документации этого модуля эта роль закреплена за секцией context: конфигурационного файла openspec/config.yaml. Рекомендация документации: на большом codebase с устоявшимися конвенциями заполните context: - и каждое предложение начнет уважать ваш стек и паттерны:

yaml
# openspec/config.yaml
context:
  # короткие, фактические указания о стеке, конвенциях и границах -
  # ровно в объеме, который делает предложения уместными

Полная настройка конфигурации проекта - тема урока 15.13. Кастомизация здесь подчиняется той же логике, что и все остальное в этом уроке: не «опишите проект целиком», а «дайте минимум указаний, после которого предложения перестают быть generic».

Инженерный вывод курса: обратите внимание на симметрию. В уроке 2.10 контекст проекта жил в openspec/project.md; в актуальном срезе документации роль начального контекста выполняет секция context: файла openspec/config.yaml (сам project.md в дереве из урока 15.2 уже не фигурирует). Инструмент развивается - сверяйте детали со своей версией CLI, а при чтении старых материалов про OpenSpec проверяйте, о каком срезе идет речь.

Несколько честных предостережений

Документация завершает руководство списком предостережений - все четыре выводятся из модели «от изменения к спецификации».

Сопротивляйтесь желанию дописать спецификации задним числом для всего. Написание спецификаций для кода, который вы не меняете, кажется продуктивным - и обычно не является. Такие спецификации протухают, потому что ничто не заставляет их следовать за реальностью: код меняется без них. Пусть реальные изменения управляют спецификациями.

Держайте первые изменения маленькими. Первые изменения - в такой же мере про освоение ритма, как про поставку. Узкий скоуп делает цикл быстрым, а уроки дешевыми. Типовые размеры первых изменений и как их удерживать - в уроке 15.10.

Коммитьте openspec/ в git. Спецификации и архив принадлежат системе контроля версий рядом с кодом, который они описывают.

Дайте агенту контекст. На большом codebase с сильными конвенциями заполните context: в openspec/config.yaml (раздел выше) - каждое предложение будет учитывать ваш стек и паттерны с первого дня.

Сведем страхи внедрения и ответы на них в таблицу:

Страх или импульс Ответ модели brownfield-first
«80 000 строк, надо описать все сначала» Спецификации пишутся только под текущее изменение; остальное накопится
«Хочу сразу back-fill спецификациями весь код» Такие спецификации протухают; пусть изменения управляют specs
«У нас есть PRD/SRS - конвертировать?» Нет: это материал для explore, дельта рождается из реального изменения
«Первое изменение - перепишем модуль X» Первые изменения держите маленькими: ритм важнее размаха
«Может, не коммитить openspec/ в git?» Спецификации и архив живут в version control рядом с кодом

Инженерный вывод курса: самый частый анти-паттерн внедрения - «сначала приведем документацию в порядок, потом начнем». Он разбивается о тот факт, что «приведение в порядок» без изменений кода не имеет естественного конца и не создает механизма, который удерживал бы написанное актуальным. У модели «от изменения к спецификации» такой механизм есть по построению: каждое изменение обязано пройти через дельту, поэтому актуальность поддерживается самим workflow, а не отдельной дисциплиной.

Карта внедрения: куда идти за деталями

Материал этого урока - карта внедрения; каждый его фрагмент раскрыт в соседних уроках модуля:

Ситуация при внедрении Куда идти
Полный разбор первого изменения от идеи до архивации Урок 15.2
Устройство living specs, changes, дельт и архивации Урок 15.3
Подбор комбинации команд под тип задачи (фича, багфикс, рефакторинг) Урок 15.8
Как держать изменения маленькими и ревьюить план до кода Урок 15.10
Командная работа: change ↔ branch ↔ pull request; Stores (beta) Урок 15.12
Конфигурация проекта, секция context:, кастомные схемы Урок 15.13

Практика

  1. Возьмите реальный проект - лучше тот, над которым работаете сейчас. Выполните cd в его корень и запустите openspec init. Посмотрите, что появилось.
  2. Выберите первое изменение по трем критериям из этого урока: нужно на этой неделе; затрагивает одну область; объясняется в двух предложениях. Если ничего не приходит в голову - возьмите задачу из бэклога, а не выдумывайте игрушечную.
  3. Выполните /opsx:explore и попросите агента прочитать затрагиваемую область. Проверьте: называет ли он реальные имена файлов и слоев вашего проекта?
  4. Выполните /opsx:propose <change-name>, проверьте план, затем /opsx:apply и /opsx:archive.
  5. Посмотрите на git status: что появилось в openspec/specs/ и в openspec/changes/archive/? Закоммитьте каталог openspec/.
  6. Оцените результат: сколько строк спецификации описывает ваш проект после первого изменения - и сколько потребовалось бы, чтобы «описать всё» до старта.
  7. Если не хочется выбирать изменение вручную - включите expanded profile (openspec config profile, затем openspec update) и запустите /opsx:onboard: команда сама найдет небольшое безопасное улучшение и проведет цикл с комментариями. Результат можно оставить или отбросить.
  8. Ответьте письменно: какой домен появился в openspec/specs/ и какие еще домены вы ожидаете через месяц - исходя из реального бэклога, а не из структуры кода.

Краткое резюме

Внедрение OpenSpec в существующий проект не начинается с документирования всего codebase - оно начинается с первого реального изменения и идет от изменения к спецификации.

  • Пишете спецификации только под то, что меняете; openspec/specs/ стартует почти пустым и накапливается.
  • Delta-first (ADDED / MODIFIED / REMOVED) позволяет специфицировать одно изменение точно, без сорокастраничной спецификации вокруг него.
  • Первое изменение: маленькое, настоящее, из плана на эту неделю; /opsx:explore сначала читает затронутую область.
  • /opsx:onboard (expanded profile) проводит весь цикл по вашему коду с комментариями.
  • Существующие PRD/SRS/TLA+ - исходный материал для exploration, а не то, что конвертируется целиком.
  • Домены в openspec/specs/ создаются по мере необходимости; монорепозиторию хватает одного корневого openspec/; для межрепозиторной работы - Stores (beta).
  • Не back-fill-ите всё, держите первые изменения маленькими, коммитьте openspec/ в git, заполните context: в openspec/config.yaml.

Вопросы для самопроверки

  1. Почему OpenSpec не требует описывать весь codebase до начала работы? Как openspec/specs/ наполняется со временем?
  2. Что такое дельта-спецификация и почему формат delta-first особенно точно соответствует brownfield-изменениям?
  3. По каким критериям стоит выбирать первое изменение на реальном codebase и почему первые изменения рекомендуют держать маленькими?
  4. Что документация советует делать с уже существующей документацией требований (PRD, SRS, TLA+-моделями) и почему массовая конвертация считается вредной?
  5. Какие способы нарезки доменов перечисляет документация и каким правилом предлагается выбирать между ними?
  6. Какая модель организации рекомендуется для монорепозитория? Что предлагается, когда работа выходит за пределы одного репозитория, и каков статус этой функциональности?

Что дальше

Урок 15.12 — «Командная работа и Stores (beta)»: соответствие change ↔ branch ↔ pull request, ревью плана до кода и планирование в отдельном репозитории, когда фича расползается на несколько кодовых баз.

Источники

Обновлено 03.10.2026