JavaRush /Курси /Claude code /Доказ паритету поведінки

Доказ паритету поведінки

Claude code
Рівень 29 , Лекція 2
Відкрита

1. Зелена збірка — це не сертифікат

Коли ви лише починаєте працювати з міграціями, дуже хочеться вважати зелений build гарною новиною достатнього масштабу. Проєкт зібрався, тести не розлетілися, IDE не червона — хочеться видихнути. У міграції ця спокуса найнебезпечніша. Зелений build каже рівно одне: «цей код у цьому середовищі зміг перетворитися на виконуваний артефакт». Нижня межа адекватності, а не сертифікат збереженої поведінки.

Паритет поведінки (feature parity) означає зовсім інше: за узгодженим набором сценаріїв зовнішня, спостережувана поведінка залишилася тією самою. Що всередині javax* стало jakarta* — користувачеві й бізнесу байдуже. Їм важливо, що GET /reports/monthly повертає той самий статус, форму відповіді, значення, дати й суми — і не починає говорити на діалекті «ну майже як раніше».

Дуже типовий приклад у міграціях на Spring Boot 3.x: проєкт компілюється, але з відповіді зникає поле, яке раніше приходило як null. Frontend перестає показувати колонку, аналітик вирішує, що система «нічого не знає» про метрику. Код не впав. Поведінка змінилася.

Зручно тримати в голові таку таблицю:

Слабкий аргумент Що він насправді означає Сильний аргумент
build
пройшов
код зібрався ті самі сценарії дали ті самі спостережувані результати
«локально відкривається» один ручний запуск не впав smoke, integration та contract checks підтверджують поведінку в pilot-розрізі
«Claude пише, що все нормально» гіпотеза на основі аналізу логи, тести, diff результатів і звіт із доказами
один тест зелений один датчик не спрацював набір датчиків покрив узгоджені сценарії

Саме тому сьогодні ми будемо дивитися на validation як на систему доказів, а не як на втішний ритуал «прогоню ще тести, раптом стане спокійніше».

2. Порівнюємо спостережувану поведінку

Перед запуском перевірок корисно зупинитися й дуже чесно відповісти на запитання: що саме ви порівнюєте між старим і новим стеком? Якщо відповідь звучить як «ну загалом усе» — ви вже потрапили в туман. Порівнювати треба конкретний набір зовнішніх ефектів pilot-розрізу.

Для нашого поточного контексту — CashFlow Dashboard, pilot у модулі reports — спостережувана поведінка це статус відповіді, JSON-структура, порядок і формат грошових значень, обробка порожніх даних, коректність періоду в звітах, рендер PDF-вивантаження на тестових прикладах. А імена імпортів, оновлені анотації, внутрішні бін-конфігурації, пакет-серіалізатор у паритет не входять — внутрішня кухня.

Щоб порівняння було чесним, стара й нова версія мають проходити одні й ті самі перевірки на тих самих даних. Різні seed-дані — і ви порівнюєте не стек, а погоду. Ось чому characterization suite, яку ви збирали раніше на етапі модернізації, тут раптово стає золотом: вона вже фіксує поточну поведінку — прогоніть той самий набір датчиків на новому стеку.

# Старий стек
git switch main
./gradlew test --tests 'reports.MonthlyRevenue*'   # 12 тестів, усі пройшли

# Новий стек
git switch migration/boot3-pilot
./gradlew test --tests 'reports.MonthlyRevenue*'   # 12 тестів, усі пройшли

Це вже краще, ніж просто «я запустив усе на новій гілці». Але ще сильніше працює порівняння конкретного результату:

# Знімаємо baseline зі старого стеку
git switch main
curl -s "http://localhost:8080/reports/monthly?period=2025-04" | jq -S . > /tmp/old.json

# Знімаємо результат із нового стеку
git switch migration/boot3-pilot
curl -s "http://localhost:8080/reports/monthly?period=2025-04" | jq -S . > /tmp/new.json

diff /tmp/old.json /tmp/new.json   # порожній вивід = відмінностей немає

Це дуже земна, майже нудна перевірка. І саме тому вона цінна. Міграції взагалі люблять нудних людей: тих, хто робить однакові виміри до і після.

flowchart LR
    A[Базова точка на старому стеку] --> B[Ті самі перевірки на новому стеку]
    B --> C{Поведінка збіглася?}
    C -- так --> D[Go або Continue pilot]
    C -- частково --> E[Виправити і повторити перевірку]
    C -- ні --> F[No-go або Rollback]

Якщо ви тримаєте цю схему в голові, validation перестає бути полюванням на баги й стає зрозумілим порівняльним експериментом.

3. Ланцюжок перевірок: датчики з доказовою силою

Коли мова заходить про validation, легко захопитися крайнощами. Один обмежується build і локальним запуском, інший робить із одного endpointʼа мінікосмодром. Перевірок має вистачати на pilot scope, але не топити вас у шумі. Для migration-пілота зручно мислити не «списком тестів», а ланцюжком доказів: кожен шар відповідає на своє запитання.

Перевірка На яке запитання відповідає Чим підтверджуємо
build
проєкт узагалі збирається на новому стеку? лог збирання
цільові тести чи не зламалася локальна бізнес-логіка в pilot-розрізі? результати unit/characterization tests
integration / contract чи збереглися зовнішні контракти й зв’язки модулів? integration suite, JSON diff, API checks
smoke чи живий критичний шлях у середовищі, близькому до реального? HTTP 200/4xx/5xx, базовий користувацький сценарій
manual check чи не змінилася річ, яку автоматикою поки не зловити? ручна звірка PDF, таблиці, візуального звіту

Зверніть увагу: manual check тут не «тестів не вистачило, сподіваємося на удачу». Є типи виходів, які дешевше й чесніше перевірити очима: три sample PDF — писати заради них систему візуального регресу занадто, а звірити вручну нормальною інженерною угодою.

Дуже важливо не перетворювати AI-згенеровані тести на автоматичний доказ: якщо тест закріплює неправильну поведінку, зелений прогін доводить лише, що ви елегантно автоматизували помилку. Читайте AI-тест як звичайний код: які входи, які assertions, чому цю поведінку вважають коректною.

4. Хмарне ревʼю

Сюди ж логічно підключити хмарне ревʼю — як додатковий аналізатор сумісності. Міграційні PR-и зазвичай великі: десятки або сотні файлів із переписаними шляхами імпортів (javax.*jakarta.*), замінами застарілих API, змінами конфігурації та оновленнями версій у build-файлах.

На такому обсязі diff локальна самоперевірка й звичайний reviewer subagent легко пропускають регресії сумісності — забуті вилучення застарілих API, зміни, що ламають транзитивні залежності, анти-патерни конкретного фреймворку в новій версії та відсутні оновлення конфігурації під нові defaults. Хмарне ревʼю (умовно /ultrareview або аналог — імʼя й доступність змінюються від версії до версії) доречне тут саме як додатковий шар поверх вашого деталізованого validation-ланцюжка.

Що воно робить на міграційному PR. Кілька агентів проходять по diff із фокусом на ламаючі зміни з migration guide; reviewer-agents із безпеки та з фреймворку вже знають патерни цільової версії. Ревʼю помічає API, які ще компілюються, але в наступній версії вилучені; перевіряє, що конфіги оновлені під нові defaults (властивості application.yml у Spring Boot 3 переїхали з javax.persistence на jakarta.persistence); ловить регресії тестів — коли вони зелені, але дьоргані вже застарілий API. На виході — розмічений за серйозністю diff, що доповнює детерміновану перевірку (build, tests, lint, CI) на семантичному рівні.

За дозволами все звично: хмарному агенту потрібен доступ лише на читання до міграційної гілки, без деплою в продакшн, без керування секретами й без запису в загальну інфраструктуру — це узгоджується з обмежувальними дозволами для агента міграції з попередньої теми. І, як ми вже домовилися раніше в курсі про витік чутливих даних через хмарні поверхні: якщо в коді лежать секрети, персональні дані клієнтів або конфіги з продакшн-доступами — цей цикл не для хмари. Ревʼю не скасовує доказів паритету: build, tests, integration, smoke і ручні перевірки все одно обовʼязкові.

На практиці корисно ставити собі трохи занудне, зате рятівне запитання: «Якщо цей баг досі існує, чи може мій набір перевірок його не помітити?» Відповідь «так» — validation suite ще не готова, навіть якщо IDE вже втомилася на вас дивитися.

5. Структура MIGRATION_VALIDATION_REPORT.md

Гарний validation report — це не красиво оформлена переможна промова, а документ, за яким інша людина відновить, що перевірили, чим це підтверджується, які діри залишилися й яке рішення ухвалили. Якщо за звітом не можна зрозуміти, що було зроблено, це не report, а терапевтична нотатка автора для власного спокою.

Зручний каркас:

# MIGRATION_VALIDATION_REPORT.md

Дата: 2026-05-24
Відповідальний: @dashboard-tech-lead
Pilot slice: reports/monthly
Підстава: MIGRATION_PLAN.md, ROLLBACK.md, COMPATIBILITY_MATRIX.md

## Прогнані перевірки
## Докази паритету фіч
## Збої та фікси
## Відомі обмеження
## Залишкові ризики
## Рекомендація

Цей шаблон короткий, але закриває головне. Угорі корисно вказати дату, відповідального, pilot slice і посилання на артефакти — це здається дрібницею, доки через місяць ви не відкриваєте три схожі файли й не гадатимете, який із них про поточний pilot.

Checks run відповідає на запитання «що саме прогнали» — конкретно, а не «ми все перевірили». У секції Feature-parity evidence ви вже не перелічуєте кроки, а показуєте, що саме збіглося.

Failures and fixes особливо корисна, бо робить звіт чесним: у гарного pilot-розрізу бувають проміжні падіння, і ви їх не ховаєте, а фіксуєте, як розбіжність було знайдено й закрито.

А ось дві секції, які новачки люблять недолюблювати, бо вони заважають красиво перемогти: Known limitations і Remaining risks. Вони дуже різні, і плутати їх не можна.

Known limitations — це свідомі межі поточного розрізу. CSV-експорт не входив у pilot; перевірені monthly reports, quarterly залишено на наступний розріз; PDF порівняно лише на трьох еталонних кейсах. Не ризик, а межа роботи.

Remaining risks — це те, що може вплинути на рішення, але поки не доведено до кінця. Staging не відтворює production-навантаження кінця місяця; один зовнішній клієнт замокано; реальні дані за грудень, де historically були timezone-аномалії, не прогнані. Зона невизначеності, яку reviewer має бачити.

Саме з цих секцій і випливає Recommendation. Не поетична — однозначна.

6. Claude як reviewer, а не як суддя

Коли у вас уже є готовий report, дуже спокусливо попросити Claude: «Ну, оціни, все добре чи ні». Тут важливо не віддати йому роль судді. Памʼять на шаблони й око на пробіли в нього непогані, але істина не в ньому, а в логах, тестах, diff результатів і заздалегідь узгоджених критеріях. Найкорисніший режим — reviewer: цифровий колега, який уміє бути неприємно уважним, коли ви втомилися й готові написати «ну начебто норм».

Такий запит працює набагато краще за абстрактне «перевір міграцію»:

Перевір мій MIGRATION_VALIDATION_REPORT.md.

Знайди прогалини:
- чи всі сценарії з pilot scope покриті;
- чи всюди є evidence, а не тільки слова;
- чи явно розділені known limitations і remaining risks;
- чи однозначна recommendation.

Файли не змінюй.
Нових перевірок без обґрунтування не вигадуй.

Якщо у вас уже є Workflow Kit і reviewer-agent, логіка та сама: нехай агент читає report, MIGRATION_PLAN.md, ROLLBACK.md і за потреби логи. Але фінальне рішення щодо recommendation ухвалюєте ви, а не система, що сама себе затверджує: самозатвердження — улюблений вид спорту поганих міграцій.

І ще одна фраза, яку корисно буквально запамʼятати: лог збирання важливіший за гарне пояснення Claude. Claude пише, що проблему усунено, а integration suite й далі червона — отже, не усунено. Все інше — літературний жанр.

7. Вибір рішення за підсумками pilot

Найважливіший момент наприкінці — обрати формальне рішення. Тут часто починається магія мови: «ну загалом можна рухатися», «нібито непогано», «мабуть, ок» — потім не зрозумієш, що ви вирішили. Тому корисно обмежити себе чотирма варіантами.

Recommendation Коли обираємо Що це означає на практиці
go
pilot довів parity за agreed scope, ризики прийнятні можна масштабувати підхід або вважати цей розріз успішним
continue pilot
базовий розріз успішний, але охоплення ще занадто вузьке для загального рішення розширюємо pilot на сусідню безпечну ділянку
no-go
у поточному вигляді pilot не готовий до продовження, але відкат не обовʼязковий не масштабуємо, доопрацьовуємо гіпотезу або план
rollback
спрацювала abort condition або порушено критичний контракт відкочуємося до попереднього working state

Тут є тонка, але важлива різниця між no-go і rollback. No-go не завжди означає, що все палає: інколи pilot просто не набрав доказів, або знайшовся дефект, який дешевше виправити на гілці без гучних рухів. Продовжувати не можна, але дослідження й виправлення всередині pilot-гілки — можна.

Rollback — це вже дія: повернення системи в попередній робочий стан за правилами з ROLLBACK.md. На staging це вимкнення feature flag, повернення версії залежності, відкат гілки. У production — окремий вид поваги до реальності.

На розрізах рівня CashFlow Dashboard дуже частим підсумком буде саме continue pilot. Це не боягузтво: розріз підтвердив підхід, але ще недостатній, щоб оголошувати перемогу по модулю.

8. Валідація на CashFlow Dashboard

Тепер зберімо все разом. На минулій лекції ви обрали pilot slice reports/monthly: read-only endpoint, є integration tests, не зачіпає billing logic напряму, і на ньому дешево ловити несумісності Boot 3.x + Java 21. ROLLBACK.md готовий, abort conditions прописані, worktree ізольовано. Після міграції та прогону перевірок фрагмент звіту міг би вийти таким:

## Прогнані перевірки
- `./gradlew clean build` на новому стеку — успішно
- `./gradlew test --tests 'reports.*'` — 47/47
- integration suite `reports-it` — 12/12
- smoke: `GET /reports/monthly?period=2025-04` у staging — 200
- ручна звірка 3 PDF-звітів із baseline

## Докази паритету фіч
- diff JSON old/new — порожній
- characterization suite зелена на старому і новому стеку

## Збої та фікси
- після міграції змінилася поведінка серіалізації `null`-полів
- виправлено через конфігурацію Jackson, перевірки повторено

## Рекомендація
continue pilot

Чому тут не go? Бо pilot покриває лише monthly reports. CSV-експорт не входив в охоплення. Квартальний сценарій не перевірявся. Production-навантаження кінця місяця не відтворено. Для цього розрізу доказів досить, для всього блоку reports — ще ні. Саме тому continue pilot тут чесніше за переможний марш.

Якщо захочете додати другу пару очей, можна попросити Claude перевірити саме повноту звіту, а не саму реальність:

Перевір цей фрагмент MIGRATION_VALIDATION_REPORT.md.
Скажи, чи достатньо evidence для recommendation = continue pilot.
Особливо перевір, чи не змішані known limitations і remaining risks.
Нічого не переписуй, поверни лише зауваження.

І ось у цей момент pilot перестає бути «ми, здається, оновили Spring Boot» і стає інженерним артефактом: baseline, ті самі перевірки до і після, ланцюжок документів, відповідальний, формальне рішення. А отже, є головне, заради чого варто займатися міграціями професійно: не лише рух уперед, а й доказ того, що ви не зламали те, що обіцяли зберегти.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ