Почему легаси ломает greenfield-допущения

В прошлом уроке мы провели фичу CSV-экспорта через полный цикл на чистом проекте. Там всё шло гладко: контракты задавали мы сами, архитектура была открытой, clarify снял неоднозначности ещё до плана.

В легаси всё устроено иначе. Три ключевых отличия:

  • Поведение уже закреплено. Пользователи зависят от того, как система работает сейчас — включая баги, которые давно стали «фичами».
  • Контракты не задокументированы. Форматы токенов, побочные эффекты, порядок вызовов — всё живёт в коде и в головах нескольких людей.
  • Писать спеку на весь монолит нереалистично. Спека описывает только изменение — дельту.

Claude Code читает кодовую базу перед генерацией спеки, и это сильная сторона. Но если кодовая база большая и запутанная, агент получит лишь частичную картину. Спека окажется оторванной от реальности ещё до первого plan.md. Выход — приём «сначала research doc».

Quick recall
Почему нельзя написать полную спеку на весь легаси-монолит в начале проекта?

Шаг 0+: research doc как «сжатый вид» кодовой базы

Прежде чем запускать /speckit.specify, просим Claude Code исследовать релевантную часть системы и зафиксировать выводы:

Исследуй модуль авторизации: точки входа, middleware,
формат токенов, способы инвалидации сессий.
Запиши в specs/research/auth-module.md.

Результат — research.md, который агент потом автоматически подхватит как контекст для спеки:

# Research: Auth Module


::widget{id="rc-2"}

## Entry Points
- POST /auth/login → AuthController.login()
- POST /auth/refresh → AuthController.refresh()

## Token Format
JWT, RS256, TTL: 15 мин (access), 7 дней (refresh)
Claims: userId, roles[], sessionId

## Session Invalidation
- Logout: добавляет sessionId в Redis blacklist
- Password change: инвалидирует все сессии пользователя
- ВАЖНО: refresh-токен не инвалидируется при обычном logout

Последняя строчка — именно то, что нигде не было задокументировано. Без research doc этот нюанс всплыл бы в code review или уже в проде. С ним — spec.md знает о раздельной инвалидации токенов не потому что угадал, а потому что прочитал.

Check yourself
Предположите: что пойдёт не так, если в большом легаси-проекте запустить `/speckit.specify` без предварительного research doc?

Спека описывает только дельту

В легаси-проекте spec.md фиксирует что именно меняется, а не всю систему. Явная секция Out of Scope — обязательная часть:


::widget{id="rc-3"}

## Scope
Добавить эндпоинт «выход со всех устройств».
Существующий flow логина/рефреша — без изменений.

## Out of Scope
- Изменение формата токенов
- Миграция хранилища сессий
- Изменения в эндпоинтах login/refresh

Out of Scope — не формализм. Он страхует от классического «а давайте заодно…» и не даёт plan.md неожиданно разрастись на три спринта. В greenfield архитектуру только создают, границы гибкие. В легаси scope creep — реальная угроза на каждой итерации: смежный код всегда манит к себе.

Check yourself
Почему секция `Out of Scope` в легаси-спеке важнее, чем в greenfield?

/speckit.converge: код против спеки

После реализации — или в середине, если задачи выполнялись частями — запускаем:

/speckit.converge

Команда сравнивает реальный код с spec.md и tasks.md и выдаёт отчёт:

✓ AC-1 (logout endpoint) → реализован
✓ AC-2 (access token invalidation) → реализован
⚠ AC-3 (refresh token invalidation) → не найдено в коде
  → Предлагаю T5: invalidate refresh tokens on logout-all

✗ Найдено в коде: email-уведомление при logout-all
  → Не было в spec.md. Задуманное поведение?

В легаси расхождения случаются чаще, чем в greenfield: разработчики нередко добавляют «очевидные» вещи прямо в процессе, не возвращаясь к tasks.md. converge ловит это системно, а не через случайный взгляд ревьюера.

Инкрементальное наращивание охвата

Не нужно покрывать спекой весь проект сразу — это и нереалистично, и контрпродуктивно. Рабочий ритм выглядит так:

1. Новая фича → research doc → полный цикл

2. Следующая фича в том же модуле → research doc уже есть, обновляем только дельту

3. Постепенно specs/research/ превращается в живую документацию реальной системы

Важный момент: research doc эволюционирует вместе с кодом. Изменили формат токена — обновили auth-module.md. Иначе следующая спека опирается на устаревший контекст и история повторяется.

flowchart TD A["Новая фича в легаси"] --> B["Research Doc\n(исследуем релевантные модули)"] B --> C["/speckit.specify\n(спека только на дельту)"] C --> D["/speckit.clarify"] D --> E["/speckit.plan"] E --> F["/speckit.tasks"] F --> G["Реализация\n(git worktrees при [P]-задачах)"] G --> H["/speckit.converge"] H --> I{"Расхождения?"} I -->|"да"| J["Уточняем задачи / спеку"] J --> G I -->|"нет"| K["Обновляем Research Doc\n(если изменились контракты)"] K --> L["Готово ✓"]
flowchart TD
    A["Новая фича в легаси"] --> B["Research Doc\n(исследуем релевантные модули)"]
    B --> C["/speckit.specify\n(спека только на дельту)"]
    C --> D["/speckit.clarify"]
    D --> E["/speckit.plan"]
    E --> F["/speckit.tasks"]
    F --> G["Реализация\n(git worktrees при [P]-задачах)"]
    G --> H["/speckit.converge"]
    H --> I{"Расхождения?"}
    I -->|"да"| J["Уточняем задачи / спеку"]
    J --> G
    I -->|"нет"| K["Обновляем Research Doc\n(если изменились контракты)"]
    K --> L["Готово ✓"]
Brownfield SDD: цикл с research doc и /speckit.converge

Параллельная работа через git worktrees

Когда несколько задач из tasks.md помечены [P] и их реализуют одновременно, конфликты файлов — типичная проблема. Решение — git worktrees:

# Создаём отдельное рабочее дерево для каждой параллельной задачи
git worktree add ../feature-t2 -b feature/logout-all-t2
git worktree add ../feature-t3 -b feature/logout-all-t3

Каждый агент (или разработчик) работает в изолированном дереве без конфликтов. После завершения — стандартный PR и merge. Для небольшой фичи это обычно лишнее, но при крупных параллельных изменениях такой подход экономит нервы.

Схема git worktrees: два изолированных рабочих дерева ответвляются от основного репозитория и развиваются параллельно, не конфликтуя друг с другом.Source: gitkraken.com

Упражнение: спланировать ввод SDD в легаси-репозиторий

Представьте: монолитный Rails-проект, ~80 000 строк, без документации, тесты частично сломаны. Нужно добавить «экспорт истории транзакций в PDF».

Перед тем как разобраться с артефактами ниже — попробуйте самостоятельно ответить: какие модули стоит исследовать первыми? (Подсказка: транзакции, генерация отчётов, права доступа.)

Теперь распределите артефакты по нужным местам:

Главное

  • Research doc даёт агенту точный контекст до того, как он начнёт генерировать. Без него спека оторвана от реальности.
  • Спека в легаси — это дельта. Out of Scope защищает от scope creep на каждой итерации.
  • /speckit.converge ловит расхождения между кодом и спекой. Запускайте минимум раз в середине реализации, не только в конце.
  • Охват наращивают инкрементально: specs/research/ со временем становится живой документацией проекта.

SDD не требует останавливать команду и переписывать всё с нуля. Достаточно правильно делать каждую следующую фичу — и через несколько месяцев легаси перестаёт быть terra incognita.