JavaRush /Курси /Claude code /Migration discovery і точка старту

Migration discovery і точка старту

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

1. Migration discovery як захист від хаосу

Оцінювання поточного стану легко сприйняти за паперову розминку перед «справжньою роботою». Але в міграціях справжня робота якраз і починається з того, що ви перестаєте гадати. Не з правок у build.gradle, а з точного відповіді — з якої технічної точки проєкт стартує просто зараз.

Коли ви мігруєте проєкт, ви змінюєте не лише код. Ви змінюєте домовленість між кодом, збиранням, середовищем виконання, бібліотеками, конфігами, тестами та CI. Не зафіксуєте її до першого upgrade — потім не скажете чесно, що зламала міграція, а що ламалося й раніше. Таке буває моторошно часто: команда оновлює Spring Boot, а за годину зʼясовується, що частина тестів тиждень червона, просто всі встигли філософськи звикнути.

Корисно думати про migration discovery як про інвентаризацію перед переїздом офісу. Скажете «столи є, компʼютери начебто теж, вантажимо» — у день переїзду серверна стійка не пройде в ліфт, принтер виявиться на древньому перехіднику, а ключ від архіву буде в людини у відпустці. У проєкті роль цих сюрпризів відіграють Gradle Wrapper, несумісні плагіни, приховані транзитивні залежності, старі API та конфіги, про які згадують лише тоді, коли вони починають горіти.

Тому питання discovery не «як би красиво оновитися», а більш приземлений — що в нас є насправді. Перший професійний фільтр проти хаосу.

2. Migration Current State vs звичайна інвентаризація

На цьому етапі багато хто намагається повторно використати старий CODEBASE_INVENTORY.md: «карта проєкту ж є». Формально так, практично — замало. Inventory і migration current state відповідають на різні питання — плутати їх так само корисно, як плутати паспорт і медичну картку.

Що порівнюємо
CODEBASE_INVENTORY.md
Migration Current State
Головний фокус Архітектура, модулі, точки входу, потоки виконання Версії, збирання, середовище виконання, конфіги, CI, застарілі API
Основні джерела Код, тести, маршрути, структура каталогів build.gradle, wrapper, docker-compose.yml, CI, application.yml, вивід команд
Головне питання «Як улаштовано проєкт?» «З якої технічної точки ми стартуємо?»
Підсумковий сенс Навігація codebase Основа для планування міграції

Гарна новина в тому, що один документ не скасовує інший — навпаки, вони добре працюють у парі. Старий inventory дає карту території. Current state — температуру повітря, стан мосту та список доріг, закритих на ремонт.

3. Докази для CashFlow Dashboard

Далі — найкорисніша частина. Щоб inventory був не «відчуттям проєкту», а опорою, збирайте evidence з конкретних файлів і команд. У CashFlow Dashboard цікавий не абстрактний «legacy-репозиторій», а все, що змінює поведінку під час зміни стеку.

Спочатку майже завжди дивляться в build.gradle — там велика частина правди про framework, плагіни та прямі залежності:

plugins {
    id 'java'
    id 'org.springframework.boot' version '2.7.18'  // Поточна лінія Boot
}

sourceCompatibility = '1.8' // Проєкт реально живе на Java 8

Навіть цей крихітний фрагмент уже дає два стартові умови: Spring Boot 2.7.18 і Java 8. Не зафіксуєте — будете обговорювати Boot 3.x, не помітивши, що він у принципі не дружить із Java 8.

Далі майже завжди відкривають wrapper, тому що версія Gradle — не декоративна дрібниця, а частина середовища збирання.

distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-bin.zip
# Wrapper фіксує поточну версію Gradle для всієї команди

Цей файл особливо корисний тим, що відрізає спокусу «локально стоїть новий Gradle, отже проєкт майже сучасний». Source of truthwrapper, а не настрій вашої машини.

Далі потрібен погляд на інфраструктурні файли, навіть якщо ви поки не збираєтеся «мігрувати інфраструктуру». docker-compose.yml одразу показує database baseline:

services:
  db:
    image: postgres:12  # Поточна база проєкту
    ports:
      - "5432:5432"

Це важливо не лише через версію PostgreSQL. Частина поведінки буває привʼязана до старого діалекту БД, до серіалізації часу, до старих індексів. На discovery це не лагодять — визнають частиною поточного середовища.

Дуже корисне джерело — CI: він часто показує те, що локальна машина розробника сором’язливо приховує, — іншу JDK, інший крок збирання, інший профіль запуску.

- name: Запуск тестів
  run: ./gradlew clean test  # Команда, яку проєкт реально вважає базовою

Якщо в CI одна версія Java, а локально всі запускають іншу, це вже не просто технічна деталь, а майбутнє джерело міграційних привидів. Ловіть їх у документі, а не вночі після upgrade.

Нарешті, не забувайте про конфіги застосунку. Навіть невеликий шматок application.yml буває важливіший за великий сервіс, якщо властивості змінювалися між major-версіями:

spring:
  jackson:
    serialization:
      WRITE_DATES_AS_TIMESTAMPS: false  # Поточна поведінка дат

Такі параметри особливо неприємні тим, що довго виглядають безневинно, а потім раптово змінюють поведінку API або тестів. Тому assessment — не лише про залежності, а й про те, як проєкт живе на налаштуваннях.

4. Прямі й транзитивні залежності проєкту

«У нас небагато залежностей» кажуть, дивлячись лише на dependencies у build-файлі. Це як «я переїжджаю найлегше», дивлячись на валізу й ігноруючи антресоль, гараж і пʼять коробок із проводами «про всяк випадок». У міграціях ці коробки називаються транзитивними залежностями.

Пряму залежність ви записали в build.gradle самі. Транзитивна тягнеться за нею. На практиці саме transitives часто влаштовують вечірку з розбитими шибками: ви про них не думали, а вони давно живуть у проєкті як повноправні мешканці.

Щоб побачити реальну картину, мало читати build-файл — потрібно ще зберігати вивід команд як evidence:

./gradlew -version > evidence/gradle-version.txt      # Фіксуємо реальні Gradle і Java
./gradlew dependencies > evidence/dependencies.txt    # Зберігаємо дерево залежностей
./gradlew test > evidence/test-baseline.txt           # Бачимо базовий стан тестів

Тут важливо не те, що команди гарно виглядають. Важливо, що ефемерний стан термінала перетворюється на файли, до яких можна повернутися. Особливо це корисно, якщо ви працюєте з Claude Code: йому простіше аналізувати збережений вивід, ніж гадати, що було в консолі три повідомлення тому.

Типовий фрагмент дерева залежностей:

+--- org.springframework.boot:spring-boot-starter-web
|    \--- org.springframework:spring-web:5.3.x
\--- org.springframework.boot:spring-boot-starter-data-jpa
     \--- org.hibernate:hibernate-core:5.6.x

Навіть цей короткий шматок уже показує, що за одним стартером — цілий натовп бібліотек. Ви оновлюєте не «один Boot», а букет: Spring Framework, Hibernate, Jackson, Security і все, що прилипло. Тому assessment без дерева розповідає, кого ви запросили самі, але мовчить про гостей, що прийшли plus-one.

5. Відомі збої, застарілі API та невідоме

Готуючись до міграції, хочеться писати лише «чисті» факти — версії, файли, команди. А неприємне (тести, що падають, застарілі API, дивні самописні адаптери) мозок пропонує не помічати. Саме воно — найцінніша частина.

Почати корисно з known failures. Якщо тест уже падає до міграції, це не привід ховати його під килим — це привід записати його в baseline.

## Відомі збої
- SubscriptionIT.testProrationAtPeriodSwitch падає і на поточному стеку
- BillingReportIT нестабільний за локального часового поясу UTC+3

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

Окремо варто фіксувати deprecated API та старі точки розширення. У CashFlow Dashboard типові кандидати — старий Spring Security і javax.*:

import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // Старий спосіб налаштування безпеки
}

І другий класичний сигнал:

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Subscription {
    @Id
    private Long id;
}

Негайно лагодити не потрібно — потрібен чесний статус: тут точка ризику на старій лінії фреймворка, її враховуємо як частину current state.

І, нарешті, найцінніше — секція Unknowns. Новачки часто думають, що unknown — це визнання слабкості. Насправді — визнання реальності. Не впевнені, як поводитиметься кастомний JSON-адаптер, старий платіжний SDK або самописний Hibernate type на новому стеку — не ховайте, записуйте.

## Невідоме
- Сумісність кастомного JsonAdapter з новою лінійкою Jackson не підтверджено
- Поведінка логіки часової зони в щомісячних звітах потребує ручної перевірки
- Реакція старого payment SDK на Java 21 поки не перевірена

Гарний документ не вдає, що команда знає все: він відділяє підтверджене від припущуваного — тому на нього і можна спиратися.

6. Claude Code як аналітик, а не апгрейдер

Claude Code тут дуже корисний — якщо правильно задати роль. Не задати — і він із ентузіазмом почне пропонувати оновлення, рецепти міграції, рефакторинг і всю ту активність, від якої ми сьогодні тримаємося подалі. На discovery це не виконавець, а аналітик із хорошою памʼяттю та терпінням до нудних документів.

Гарна інструкція на цей крок:

Збери current-state inventory для репозиторію.
Джерела: build.gradle, gradle-wrapper.properties, docker-compose.yml,
.github/workflows/*, application.yml, src/main/**.
Нічого не змінюй. Не пропонуй upgrade.
Кожне твердження підтверджуй file path або виводом команди.
Усе непідтверджене винось у секцію Unknowns.

Якщо у вас уже є проєктний CLAUDE.md, туди корисно додати короткий режим саме під migration discovery:

## Режим migration discovery
- Працювати лише в read-only режимі
- Версії підтверджувати file path або виводом команди
- Не пропонувати update dependencies без явного запиту
- Усе сумнівне писати в Unknowns

Схема процесу проста:

flowchart LR
    A[Файли репозиторію] --> C[Чернетка інвентаризації]
    B[Вивід команд] --> C
    C --> D[Перевірка людиною]
    D --> E[Поточний стан міграції]

Зверніть увагу на останню стрілку. Claude не випускає документ у прод із своєї голови: готує чернетку, збирає evidence, допомагає нічого не забути — але «так, це confirmed» залишається за людиною. І це правильний баланс: AI прискорює аналіз, а не підміняє інженерне рішення.

7. Збір документа Migration Current State

Коли evidence зібрані, важливо не залишити їх жити розрізнено: шматок у терміналі, шматок у голові, шматок у переписці. Зберіть в один артефакт — не красивий, а робочий. Тримайте поруч з іншими migration-матеріалами, у docs/migrations/current-state.md.

Мінімальний каркас:

# Поточний стан міграції — CashFlow Dashboard

## Поточні версії
## Інструменти збирання та runtime
## Прямі й транзитивні залежності
## CI-команди
## Застарілі API
## Відомі збої
## Невідоме
## Нотатки про докази

А невеликий заповнений фрагмент — так:

## Поточні версії
- Java: 8
- Spring Boot: 2.7.18
- Gradle: 7.6.4
- PostgreSQL: 12

## Застарілі API
- WebSecurityConfigurerAdapter у SecurityConfig.java
- javax.persistence.* в entity-класах

## Невідоме
- Сумісність payment SDK з Java 21 не підтверджено

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

І в якийсь момент ви помічаєте дуже приємну річ. Після такого assessment проєкт перестає виглядати чорною скринькою з написом «legacy» — він стає набором конкретних фактів, обмежень і невідомого. А з конкретними фактами вже можна розмовляти без паніки — навіть якщо попереду Spring Boot major upgrade, стара база й код, який писала людина з дуже багатим внутрішнім життям.

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