1. Roadmap задаёт направление, шаги — безопасность
Когда вы только осваиваете incremental refactoring, легко попасть в ловушку: «ну мы же и так делаем маленькие коммиты, значит roadmap нам не нужен». Звучит логично — и всё равно ловушка. Разбирать старый шкаф по одной доске безопасно, но пока вы не знаете, что собираете, вы с одинаковым усердием идёте к столу и к экспонату для музея инженерных трагедий.
Дорожная карта модернизации отвечает не только на вопрос «что делаем следующим». Она отвечает и на куда более неприятные, но полезные вопросы: какой срез берём первым и почему, чем измерим успех фазы, кто примет результат, где лежит rollback и что мы сознательно не трогаем в этом цикле. Иначе модернизация вырождается в «раз уж мы тут, давайте ещё чуть-чуть улучшим» — а «чуть-чуть» в legacy любимый способ вырастить diff на 900 строк и делать вид, что так и было задумано.
Для CashFlow Dashboard это особенно важно. Есть mrr-engine, вокруг него RISK_MAP и CHARACTERIZATION_TESTS.md, найдены seams вроде PlanLookup и AuditSink, понятны candidate slices под Strangler. Но без общей структуры через неделю вы не вспомните, почему сначала вытаскивали plan lookup, а не refund handling. Legacy любит стирать мотивацию и оставлять только следы коммитов.
Вот почему roadmap — не «план на красивом слайде», а рабочий документ: он связывает локальные refactor-итерации с крупной целью — подготовить mrr-engine к будущей замене расчётного ядра, не трогая бизнес-правила и runtime/framework upgrade.
flowchart TD
A[Текущее состояние
RISK_MAP + baseline] --> B[Фаза 1
pilot slice]
B --> C[Фаза 2
следующий seam]
C --> D[Фаза 3
подтверждённая parity]
D --> E[Целевое состояние
меньше coupling, проще дальнейшие изменения]
Маленькие шаги отвечают за безопасность движения, roadmap — за направление. Без первого вы врежетесь, без второго — заблудитесь очень дисциплинированно.
2. Блоки хорошего modernization roadmap
Когда вы слышите словосочетание «дорожная карта», легко представить толстый документ на двадцать страниц, который никто не читает, кроме автора, писавшего его в пятницу вечером из чувства вины. Нам такое не нужно: roadmap должен быть коротким, привязанным к evidence и конкретным настолько, чтобы по нему работать.
В этой работе дорожную карту удобнее держать как секцию внутри REFACTOR_LOG.md — того же журнала с refactor-steps, candidate slices и evidence по parity. История модернизации не расползается по контейнерам; разрастётся — вынесете в отдельный файл. Внутри: текущая картина, целевые результаты, фазы, baseline, проверки, владельцы и явные не-цели.
| Блок | Зачем нужен | Пример для CashFlow Dashboard |
|---|---|---|
| Current state | Зафиксировать, из чего стартуем | legacy mrr-engine, есть RISK_MAP, baseline v1 |
| Target outcomes | Понять, что считаем улучшением | меньше coupling, выделены seams, проще проверять расчёты |
| Phase 1 | Pilot slice с минимальным риском | вынести PlanLookup без изменения поведения |
| Phase 2+ | Последовательность следующих шагов | изолировать AuditSink, подготовить seam для calc_core |
| Safety baseline | Не потерять поведение между фазами | characterization + golden master + smoke checks |
| Validation checkpoint | Понять, когда фаза реально завершена | mrr_total совпадает на fixtures A/B/C |
| Owners / reviewers | Убрать безличное «кто-нибудь проверит» | maintainer модуля + reviewer-agent + финансовый reviewer |
| Rollback | Сделать откат не теоретическим, а рабочим | revert одного коммита или выключение feature flag |
| Non-goals | Не дать scope расползтись | не трогаем schema, runtime, новые MRR-правила |
Обратите внимание на важную деталь: фаза в roadmap — это не абстрактное «почистить модуль», а конкретный срез плюс конкретный вид доказательства. «Улучшить читаемость mrr-engine» — пожелание. «Extract PlanLookup, baseline green, golden master на майских fixtures, diff в одном PR» — рабочая единица.
Например, так может выглядеть короткий фрагмент такого документа:
# REFACTOR_LOG.md
## Roadmap модернизации
### Текущее состояние
Legacy mrr-engine, RISK_MAP v1, characterization baseline v1.
### Фаза 1
Extract PlanLookup from calc flow.
Validation: baseline green on fixtures A, B, C.
Rollback: single commit revert.
Не героически — зато с ним можно жить, а в legacy это лучший комплимент документу.
Ещё один важный момент: roadmap не должен притворяться миграционным планом. Если в один документ въехали upgrade framework, новый runtime, schema migration и новая бизнес-логика MRR — это уже не roadmap модернизации, а жанровая смесь, заканчивающаяся нервным тиком у всей команды. Модернизация улучшает структуру, сохраняя поведение; остальное планируется отдельно.
3. Критерии успеха привязаны к evidence
С критериями успеха есть старая инженерная проблема: их очень легко красиво написать и очень трудно сделать полезными. «Код стал чище и современнее» звучит приятно, но не проверяется — примерно как «пациент чувствует себя духовно бодрее». Возможно. Но merge на этом не примешь.
Хорошие success criteria привязаны к observable evidence. В модернизации это вдвойне важно: мы не выкатываем фичу для экрана — мы улучшаем структуру, сохраняя поведение. Успех живёт в двух плоскостях: старое поведение на месте, внутренняя сложность снизилась. Рабочие критерии: characterization baseline зелёный на agreed scenarios, golden master не изменился на fixtures A/B/C, цикломатическая сложность ключевой функции упала, diff фазы читается за один review, rollback делается за один шаг, документация совпадает с новым устройством кода.
| Плохая формулировка | Рабочая формулировка | Чем проверяем |
|---|---|---|
| «Код стал чище» | calc_core разбит на 3 smaller units без смены output | diff + baseline + code review |
| «Система стабильна» | все agreed checks зелёные на fixtures A/B/C | characterization suite |
| «Модуль проще развивать» | выделен явный seam для PlanLookup и он покрыт baseline | tests + structure review |
| «Можно откатить» | rollback = один revert commit / один flag off | dry run rollback |
| «Документация обновлена» | описание flow и boundary совпадает с текущим кодом | docs review |
Полезно помнить простое правило: если критерий нельзя показать в PR description, логе CI, baseline-отчёте или коротком review — скорее всего, он слишком расплывчатый.
Claude Code здесь может очень помочь, но в своей правильной роли. Его удобно просить не «придумать красивые success criteria», а отревьюить ваши критерии на конкретность:
Review this modernization roadmap.
Find success criteria that are vague, not measurable, or not backed by existing checks.
Suggest stronger wording tied to baseline, diff review, rollback, and docs.
Do not expand scope.
Claude здесь редактор, а не автор судьбы проекта. Критерии успеха — часть инженерной ответственности, а не поэтический конкурс.
4. Non-goals, rollback и владельцы фаз
Три элемента roadmap новички почти всегда недооценивают — и именно они спасают проект от расползания: non-goals, rollback и явные владельцы фаз. Не так романтично, как Strangler Fig, зато работает даже в понедельник утром.
Начнём с non-goals. Не-цели нужны не для бюрократии, а чтобы у плана были стены; без них roadmap превращается в чемодан без молнии: вы всё докладываете, а потом удивляетесь, почему он больше не закрывается. Выделяете PlanLookup — в этот цикл не должны въехать новые MRR-правила, schema changes, framework upgrade или «заодно перепишем refund path».
## Не-цели
- no Spring/Java upgrade
- no DB schema changes
- no new MRR business rules
- no public API changes
Второй элемент — rollback. «Если что, откатим» — не стратегия, а самоуспокоение. Рабочий rollback называет действие, которым вы возвращаете систему в прошлое, и сколько это займёт. Если для отката нужно собирать созвон, вспоминать, где старый конфиг, и просить Ваню из DevOps «на минутку помочь», — срез слишком большой.
Третий — владельцы и reviewers. Фаза без владельца — не фаза, а коллективная надежда. Здесь подключается Workflow Kit: reviewer-agent делает первый проход по diff, tester-agent проверяет, что characterization suite осталась релевантной, а финальный human reviewer смотрит, не подменили ли под видом refactor изменение поведения.
| Роль | Что подтверждает |
|---|---|
| Maintainer модуля | корректность фазы и границы изменений |
| reviewer-agent | соблюдение non-goals и читаемость diff |
| tester-agent | валидность baseline и test coverage по slice |
| Бизнес-reviewer | сохранение нужного финансового поведения |
Когда эти три элемента зафиксированы, roadmap перестаёт быть списком хороших намерений и становится договорённостью. Не самой весёлой, но в legacy это важнее веселья.
5. Анти-паттерны модернизации и здоровые альтернативы
Теперь давайте честно посмотрим на то, что ломает модернизацию чаще всего. И нет, это не только «плохой legacy-код» — гораздо чаще проблемы создают хорошие намерения, плохо упакованные в workflow. Legacy сам по себе неприятен, но настоящую драму обычно устраивает человек, решивший «ускориться» без baseline, без roadmap и с одной героической командой для Claude.
Самый популярный анти-паттерн звучит так: «Claude, modernize this module nicely». В этой фразе прекрасно всё, кроме инженерного смысла: нет slice, нет non-goals, нет baseline, нет успеха, нет rollback — зато простор для творческой щедрости модели. А Claude, как вы уже заметили, очень любит быть полезным: не поставите границы — он поможет так широко, что полдня будете снимать эту помощь с diff.
Сравните плохой запрос и рабочий.
Плохо:
Modernize mrr-engine and make the code cleaner.
Хорошо:
Inspect mrr-engine and refactor only PlanLookup extraction.
Do not change observable behavior, public API, or DB schema.
Baseline that must stay green: fixtures A, B, C.
Keep the diff reviewable in one PR.
Ещё один анти-паттерн — смешанный diff. Это когда в одном PR вы разом рефакторите, меняете бизнес-правило, двигаете зависимости и «чуть-чуть готовитесь к migration». Читать, проверять, честно откатывать — невозможно. Если вы видите, что фаза породила эту смесь, проблема не в Git и не в reviewer-agent, а в декомпозиции.
| Анти-паттерн | Почему опасен | Здоровая альтернатива |
|---|---|---|
| Big-bang rewrite | теряете baseline, теряете rollback, теряете понимание diff | phased roadmap с pilot slice |
| Refactor + feature + migration в одном PR | невозможно проверить, что именно сломалось | однородные фазы и отдельные PR |
| Roadmap без non-goals | scope бесконечно растёт | явные границы цикла |
| «Тихая починка бага» внутри refactor | меняется behavior без отдельного решения | отдельная задача на change behavior |
| Огромный diff «зато один раз» | review становится формальностью | reviewable slice per phase |
| Фаза без owner | никто не отвечает за done | owner + reviewer + baseline check |
Очень коварный случай — молчаливая «починка» известного legacy-бага. Вы рефакторите код, видите странное поведение и думаете: «Ну раз уж я здесь, исправлю сразу». Сердце доброе, инженерная дисциплина плачет. Если downstream-часть или люди уже привыкли к этому поведению, вы превратили refactor в product change — нужен другой план проверки, другие критерии приёмки, отдельное решение команды. Под видом уборки нельзя переставлять стены.
Ещё один характерный симптом плохого roadmap — оторванность от evidence: если в фазе написано одно, а baseline, PR description и review notes другое, у вас бумага живёт отдельно от работы. Такой roadmap лучше сократить, чем украшать.
Когда дорожная карта собрана хорошо, она даёт очень приземлённое ощущение: вы понимаете, что делаете сегодня, почему именно это, чем проверите и где остановитесь. Не «мы перепридумали архитектуру», а спокойная уверенность, что следующий шаг не сломает предыдущий.
Именно в этом состоянии modernization перестаёт быть страшным словом — становится серией контролируемых изменений. Да, на legacy. Да, с осторожностью. Да, без героизма. Зато финал — не легенда о великом переписывании, а нормальный, проверяемый, поддерживаемый результат. И граница простая: пока roadmap держится на behavior-preserving changes, seams, parity и простом rollback внутри текущей структуры, вы в modernization. Упёрлись в compatibility matrix, framework/runtime/schema changes, phased rollout и rollback между двумя версиями системы — это уже migration workflow, с другой ценой ошибки.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ