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» — побажання. «Винести PlanLookup, baseline зелений, golden master на травневих fixtures, diff в одному PR» — робоча одиниця.
Наприклад, так може виглядати короткий фрагмент такого документа:
# REFACTOR_LOG.md
## Roadmap модернізації
### Поточний стан
Legacy mrr-engine, RISK_MAP v1, characterization baseline v1.
### Фаза 1
Винести PlanLookup з calc flow.
Перевірка: baseline зелений на fixtures A, B, C.
Rollback: 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 менші одиниці без зміни виходу | diff + baseline + code review |
| «Система стабільна» | усі agreed checks зелені на fixtures A/B/C | characterization suite |
| «Модуль простіше розвивати» | виділено явний seam для PlanLookup і він покритий baseline | tests + structure review |
| «Можна відкотити» | rollback = один revert commit / одне вимкнення flag | dry run rollback |
| «Документацію оновлено» | опис flow і boundary збігається з поточним кодом | docs review |
Корисно пам’ятати просте правило: якщо критерій не можна показати в PR description, логу CI, baseline-звіті або короткому review — швидше за все, він надто розпливчастий.
Claude Code тут може дуже допомогти, але у своїй правильній ролі. Його зручно просити не «вигадати красиві success criteria», а перевірити ваші критерії на конкретність:
Переглянь цю дорожню карту модернізації.
Знайди критерії успіху, які є нечіткими, не вимірюються або не підкріплені наявними перевірками.
Запропонуй сильніше формулювання, пов’язане з baseline, diff review, rollback і docs.
Не розширюй 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 | поетапна 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 з іншою ціною помилки.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ