1. Призначення документа «як воно працює сьогодні»
Коли ви працюєте з legacy, доволі швидко зʼясовується неприємна, але корисна правда: кодова база майже завжди знає про себе більше, ніж документація. А документація знає щось — але про молодшу й оптимістичнішу версію системи. Тому документ how it works today потрібен не як прикраса репозиторію, а як карта місцевості перед входом у туман.
Головна ідея дуже проста: ви описуєте не те, як система має працювати, а те, як вона працює зараз. Звучить майже образливо просто, але саме тут на практиці найчастіше й починається плутанина. Бачите дивну поведінку, згадуєте гарний ARCHITECTURE.md — і рука тягнеться написати «ну очевидно, пауза підписки має виключати її з MRR». Ні. Якщо в коді пауза залишається в active MRR, фіксуєте саме це.
У legacy-документації дуже приземлена задача: зменшити вартість наступного кроку. Новий інженер швидше розуміє систему, ревʼюер бачить слабкі місця в потоці грошей і даних, а той, хто полізе змінювати mrr-engine, бачить: які правила доведені кодом, які припускаються, а які треба перевіряти вручну. Не архітектурна проза — шар безпеки перед змінами.
На CashFlow Dashboard це особливо помітно. Старий BILLING_RULES.md каже, що paused subscription не враховується в активному MRR. Але якщо MrrFormulas.java робить навпаки, документ лояльний до коду, а не до мрії про прекрасне минуле. Legacy не терпить романтизму. Воно надає перевагу доказам.
2. Факти, припущення і ручна перевірка
Одна з найкорисніших звичок у роботі з legacy — перестати змішувати доведене й припущене в один гладкий текст. Коли все написано одним рівним абзацом, документ виглядає солідно, а довіряти йому не можна. Тому хороший current-state документ розкладає висновки на три кошики: підтверджені факти, припущення й те, що потрібно перевірити вручну.
Підтверджений факт — це твердження, у якого є якір доказу. Не «Claude впевнений», не «схоже за назвою методу», а конкретний файл, функція, тест, конфіг, лог або команда. Знімок MRR запускається в jobs/MrrSnapshotJob.java; paused subscription залишається в active MRR за гілкою в mrr-engine/MrrFormulas.java. Можете показати пальцем на код — це факт.
Припущення — це не помилка документації, а чесна фіксація межі знання. Після успішного retry підписка, ймовірно, вважається безперервною, але тесту немає, і в логах ви такого сценарію не бачили. Отже, це не факт. Не маскуйте це словом «ймовірно» — винесіть в окремий розділ і назвіть припущенням, що потребує перевірки.
А ручна перевірка потрібна там, де одного коду й тексту вже недостатньо: звірити поведінку на продакшн-даних, подивитися місяць із переходом через часовий пояс, обговорити правило з власником домену, підняти історичний інцидент. Не недопрацювання документа — нормальна частина чесної інженерної картини.
Наочно потік виглядає так:
flowchart TD
A[Код, тести, конфіги, логи] --> B[Чернеткові висновки Claude]
B --> C[Перевірка за evidence]
C --> D[Підтверджені факти]
C --> E[Припущення]
C --> F[Перевірити вручну]
D --> G[ARCHITECTURE_CURRENT.md]
E --> G
F --> G
А короткий фрагмент документа — так:
## Підтверджені факти
- Щоденний знімок MRR запускається за розкладом `02:00 UTC`
(`jobs/MrrSnapshotJob.java:21`).
- Пауза підписки не виключає її з active MRR
(`mrr-engine/MrrFormulas.java:88`).
## Припущення
- Після успішного retry підписка вважається безперервною,
але окремого тесту на це немає.
## Що потрібно перевірити вручну
- Поведінку розрахунку на межі місяця для підписки у статусі `paused`.
Зверніть увагу на важливу деталь: розділення зроблено фізично, а не лише тоном. Розкидайте по тексту «ймовірно» й «здається» — за тиждень ніхто не відрізнить факт від здогадки. А окремі секції розуміє навіть дуже втомлений інженер у пʼятницю ввечері.
3. Claude Code як асистент із витягання знань
Тут дуже легко звернути не туди. Claude Code швидко збирає чернетки, знаходить файли, підсумовує поведінку. Але дайте йому свободу «поясни архітектуру» — і він із задоволенням зробить текст красивим раніше, ніж точним. У legacy-документації Claude Code — асистент із knowledge extraction, а не автор фантастичного роману за мотивами вашого репозиторію.
Практично це означає дві речі. Перше: discovery і документування ведете в логіці лише читання — plan mode, пошук, Git history, аналіз тестів і логів. Друге: одразу задаєте строгий формат виводу. Claude працює краще, коли ви не просите «описати систему», а називаєте потрібні секції й спосіб маркувати висновки.
Робочий запит для такого режиму:
Збери current-state note для модуля `mrr-engine`.
Для кожного важливого твердження вкажи файл і рядок.
Розділи відповідь на:
1. Підтверджені факти
2. Припущення
3. Що потрібно перевірити вручну
Не пропонуй змін у коді й не описуй бажану поведінку.
Такий формат одночасно пришвидшує збір чернетки й не дає Claude непомітно підмінити поточну поведінку бажаною. Особливо важливо в CashFlow Dashboard, де старі документи вже конфліктують із кодом. Не заборонили описувати «як має бути» — Claude почне лагодити реальність просто в тексті. А нам потрібен не ремонт, а чесний знімок.
Якщо у вас уже є Workflow Kit з read-only агентом на кшталт discovery-explorer або акуратним documenter — чудово. Але й у цьому випадку принцип не змінюється: агент допомагає зібрати матеріал, а не затверджує його за вас. Під кожною значущою фразою все одно код, тест, конфіг або лог. Інакше це просто красиво оформлена гіпотеза.
4. Артефакти, які справді потрібні
Дуже хочеться скласти все в один великий ARCHITECTURE.md на дванадцять екранів, із трьома таблицями, двома діаграмами й сумним обличчям того, хто це читатиме. Краще так не робити. До цього моменту у вас уже є широкий CODEBASE_INVENTORY.md із першого знайомства з репозиторієм, discovery-note із поточного входу в legacy-сервіс і короткий signal log з technical signals. Останні два — сирий робочий шар. Із нього збираєте стійкий current-state шар, на який спиратимуться RISK_MAP.md і BEHAVIOR_INVENTORY.md.
| Шар | На яке запитання відповідає | Як його підтримувати |
|---|---|---|
|
Що взагалі є в проєкті | Широкий базовий зріз із раннього discovery; його не викидають, а уточнюють з урахуванням risky areas |
| discovery-note / signal log | Що вже підтверджено і що поки неясно | Тимчасовий робочий шар для поточного входу в legacy |
|
Як система реально працює сьогодні | Основний стійкий current-state документ |
|
Який зріз current-state потрібно винести окремо | Розділи всередині ARCHITECTURE_CURRENT.md або окремі піддокументи, якщо одного файлу вже замало |
|
Де зміна найбільш небезпечна і що з цим робити | Шар рішень поверх current-state |
|
Які сценарії не можна зламати під час змін | Сценарний шар для before-change baseline |
Хитрість у тому, що current-state шар тут один — основний ARCHITECTURE_CURRENT.md. MODULE_INVENTORY.md і CRITICAL_FLOWS.md не зобовʼязані жити як два незалежні всесвіти. Це просто зручні зрізи того ж самого шару, коли в одному файлі стало тісно.
Кусок module inventory може виглядати так:
| Модуль | Призначення сьогодні | Власник | Докази |
|---|---|---|---|
| `subscriptions` | життєвий цикл: trial → active → paused → cancelled | inferred: unclear | `SubscriptionService.java` |
| `mrr-engine` | щоденний розрахунок MRR/ARR | inferred: high churn | `MrrCalculator.java`, `MrrFormulas.java` |
| `payments` | PSP, retry, audit | команда платформи | `PaymentService.java`, `PaymentRetryService.java` |
Зверніть увагу на поле власника. Якщо ownership ви вивели з Git history, старих коментарів і звички одного розробника лагодити цей модуль о третій годині ночі, так і пишіть: inferred. У legacy дуже корисно не вдавати, ніби ви знаєте більше, ніж знаєте.
5. Старий ARCHITECTURE.md — залишаємо як історію
Тут майже завжди виникає спокуса «виправити документацію». Звучить шляхетно, але найчастіше це погане рішення. Старий ARCHITECTURE.md — історичний артефакт. Він може бути застарілим, неточним, місцями навіть наївним, але все одно фіксує, як команда колись думала про систему. Видаляти його або переписувати поверх — це все одно що стирати старе креслення і вдавати, ніби будівлю завжди зводили за новою версією.
Набагато корисніше зберегти старий документ як історичне джерело, а поруч створити current-state версію. Назва файлу може відрізнятися: ARCHITECTURE_CURRENT.md, ARCHITECTURE.today.md, ARCHITECTURE_NOW.md. Назва другорядна. Принцип першорядний: ви не затираєте історію, а додаєте чесний знімок поточного стану.
І дуже корисно прямо фіксувати розбіжності між історичною документацією й кодом. Це не «ганебний розділ», а один із найцінніших. Він одразу показує, де команда живе у двох версіях реальності одночасно: у текстовій і в тій, що виконується.
Наприклад, так:
## Розбіжності з історичною документацією
- `ARCHITECTURE.md`: paused subscriptions не входять в active MRR.
- Поточна поведінка в коді: paused subscriptions залишаються в active MRR
(`mrr-engine/MrrFormulas.java:88`).
- Статус: факт підтверджено кодом, правило в документації застаріло.
Такий розділ робить дві корисні речі. По-перше, він захищає наступного інженера від хибної довіри до старого документа. По-друге, прибирає вічну командну драму «але ж у документації написано інакше». Суперечити, хто правий, більше не потрібно: док каже одне, код робить інше, і це чесно задокументовано.
6. Чернетка current-state для CashFlow Dashboard
Коли у вас уже є discovery-знахідки, debt-сигнали й перші evidence anchors, зібрати чернетку ARCHITECTURE_CURRENT.md стає набагато простіше. Ви не вигадуєте архітектуру заново — перекладаєте накопичені факти в більш стабільну форму. І так, саме тут стає видно, наскільки корисно було не змішувати все докупи на попередніх кроках.
Для CashFlow Dashboard почніть із такої структури:
# ARCHITECTURE_CURRENT.md
## Що це за документ
Короткий опис: фіксує поточну поведінку системи, а не цільову архітектуру.
## Підсистеми
- subscriptions
- billing
- payments
- refunds
- mrr-engine
- reporting
## Критичні потоки
- розрахунок MRR
- retry платежу
- partial refund
- scheduled exports
## Підтверджені факти
## Припущення
## Що потрібно перевірити вручну
## Розбіжності з історичною документацією
Якщо current-state шар спокійно поміщається в один файл, MODULE_INVENTORY.md і CRITICAL_FLOWS.md залишаються розділами тут же. Окремими файлами — лише тоді, коли це справді спрощує читання.
Далі ви вже наповнюєте його змістом із конкретних модулів. Про mrr-engine можна написати короткий current-state блок, не впадаючи в епос:
## mrr-engine — як працює сьогодні
### Підтверджені факти
- Щоденний розрахунок запускається з `jobs/MrrSnapshotJob.java:21`.
- Основний вхід у обчислення — `mrr-engine/MrrCalculator.java:42`.
- Пауза підписки не виключає її з active MRR
(`mrr-engine/MrrFormulas.java:88`).
### Припущення
- Після успішного retry підписка залишається безперервною за MRR,
але окремого тесту на це не знайдено.
### Що потрібно перевірити вручну
- Поведінка розрахунку на межі місяця для призупиненої підписки.
- Поведінка після upgrade у день білінгу.
Гарний знак, якщо цей документ виглядає трохи менш красиво, ніж маркетингова презентація, і трохи корисніше, ніж загальна діаграма «у нас є backend і database». Legacy current-state має бути приземленим. Його сила не в стилі, а в щільності правди на квадратний абзац.
І тут зʼявляється, мабуть, найприємніший ефект усієї лекції. Щойно є чесний current-state по mrr-engine, payments і subscriptions, команда перестає сперечатися із системою на рівні відчуттів. У CashFlow Dashboard і далі можуть бути дивні правила, застарілі залежності та нервовий характер — але це вже не туманне колективне відчуття, а привʼязана до коду реальність, з якою працюють далі без археології на кожному кроці.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ