JavaRush /Курси /Claude code /Міграції даних і конфігурації

Міграції даних і конфігурації

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

1. Дані та конфігурація — окремий клас ризику

На цій темі багато хто вперше розуміє, чому міграції даних і конфігурації лякають навіть сильних розробників. Код відкочується комітом, а проблему видно очима компілятора. Дані та налаштування ламаються мовчки, без спецефектів, але з дорогими наслідками. Це окремий клас ризику.

І тут важливо не змішувати цю розмову з першим migration/boot3-pilot на reports/*. Там був маленький pilot лише для читання Boot 3 без змін схеми та конфігураційних стрибків. Тут приклад інший — зі своєю гілкою, backup і дисципліною відкату. Завдання не розширити той pilot до небезпечного масштабу, а показати інший клас ризику.

На прикладі CashFlow Dashboard це видно особливо добре. Під час переходу зі Spring Boot 2.7 та Java 8 на Boot 3.x і Java 21 гучні проблеми майже чесні: javax.* не компілюється, тести падають. Підступніше — дані. У модулі звітів частина подій у локальній зоні, частина — в UTC, а розрахунок MRR по-різному трактує одне поле часу. Проєкт збереться ідеально, а звіти за місяць розійдуться з реальністю на кілька відсотків. Компілятор мовчить.

Те саме з конфігурацією. Старий код читав REPORT_TZ, новий чекає cashflow.reports.default-zone, staging живе на старих змінних — локально все зелене, а в staging поведінка несподівана. Не аварія, а «щось дивно рахує».

Корисно тримати перед очима просте порівняння:

Що мігруємо Що зазвичай допомагає впіймати помилку Чим небезпечно
Код компілятор, тести, diff проблема зазвичай помітна одразу
Дані backup, звірка з baseline, validation report можна тихо спотворити бізнес-результат
Конфігурацію інвентаризація налаштувань, dry-run, owner review проєкт може «працювати», але в неправильному режимі

Саме тому для більшості студентів це насамперед тема про розуміння ризику. Немає явного доступу, відповідальності та узгодження — завдання не «героїчно виконати міграцію», а розпізнати, чому її не можна робити наскоком. Іноді професіоналізм — це просто не натискати Enter.

2. Межі безпеки до першої зміни

Якщо кодову міграцію іноді ще намагаються почати з духом «розберемося дорогою», то для даних і конфігурації такий підхід майже завжди закінчується нічним повідомленням у чат і знайомством з черговим інженером. Межі встановлюють заздалегідь.

Межа Що це означає на практиці
Явне узгодження хтось призначений знає, що міграція починається і навіщо
Backup у вас є знімок бази та поточної конфігурації до змін
ROLLBACK.md
відкат описано до старту, а не після першої аварії
Owner review відомий власник модуля або середовища, який підтверджує крок
Test data first спочатку перевіряємо на тестових даних або staging, не на production
Production boundary жодного «швиденько перевіримо на бойовому» без окремого рішення

Тут корисно думати не категоріями бюрократії, а категоріями ціни помилки. Додали поле — терпимо. Перезаписали кривим скриптом дати білінгу в підписок — і вже неважливо, наскільки гарний ваш pull request.

Саме тому навіть маленький pilot живе в окремій гілці та з окремими backup-артефактами:

mkdir -p backups
pg_dump -Fc cashflow_dashboard > backups/cashflow-pre-utc.dump   # знімок бази до міграції
cp config/application.yml backups/application-pre-boot3.yml      # знімок поточних налаштувань
git switch -c migration/reports-utc-pilot                        # окрема гілка під pilot
./gradlew test --tests 'reports.*'                               # базова перевірка до змін

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

Важливе й ще одне правило: якщо rollback або backup «не потрібен» — не пропускайте його мовчки, а заповніть як none, because ...: «data rollback: none, бо дані не змінюються». Порожнеча надто часто означає не «не потрібно», а «забули подумати».

3. Схема expand → backfill → switch → contract

Коли розмова доходить до міграції даних, у новачків часто виникає бажання або «одразу все переписати», або безкінечно відкладати. Практика любить середину — схему expand → backfill → switch → contract, де система якийсь час живе і зі старим, і з новим уявленням даних.

Візьмімо реалістичний сценарій CashFlow Dashboard. У legacy частина подій лежить у legacy_ts, а зона — окремо в legacy_timezone. Мета — єдине поле normalized_ts_utc, щоб звіти та MRR не залежали від локальних поясів і трактувань старого формату.

Логіка фаз виглядає так:

Фаза Що відбувається Наскільки реальним є відкат
Expand додаємо нове поле, нічого не ламаючи в старому читанні відкат простий
Backfill переносимо старі дані в новий формат пакетами відкат ще простий
Switch reads читання переводимо на новий стовпець через прапор відкат дуже дешевий
Switch writes нові записи пишемо і в старе, і в нове уявлення відкат усе ще можливий
Contract видаляємо старе поле після стабілізації відкат дорогий або неможливий без backup

Перший крок — не «перемкнути все на нове», а розширити схему:

ALTER TABLE subscription_payments
ADD COLUMN normalized_ts_utc TIMESTAMPTZ; -- новий стовпець під UTC

UPDATE subscription_payments
SET normalized_ts_utc = legacy_ts AT TIME ZONE COALESCE(legacy_timezone, 'UTC')
WHERE normalized_ts_utc IS NULL; -- первинний backfill старих записів

CREATE INDEX idx_sub_payments_utc
ON subscription_payments(normalized_ts_utc); -- щоб читання не стало повільнішим

Далі починається найцікавіша частина. Старе поле не вирізаємо одразу: нове заповнюється та перевіряється, застосунок живе за старими правилами. Потім читання переводять на новий стовпець через прапор — помітили розбіжність, вимкнули прапор, повернулися до старої логіки.

Наприклад, у конфігурації pilot це може виглядати так:

cashflow:
  reports:
    use-utc-column: false   # спочатку читаємо старе поле
  migration:
    dual-write-utc: false   # поки не пишемо одночасно у два формати

Пізніше перемикаєте use-utc-column: true, потім вмикаєте подвійну запис. І лише після стабілізації, коли validation report підтверджує feature parity, а моніторинг мовчить, думають про contract — видалення стовпця.

І ось тут у команди зазвичай виникає спокуса: «раз усе працює, давайте одразу видалимо legacy». Не треба. Видалення — не нагорода за поведінку, а окремий ризикований крок. Хочеться негайно — встаньте, налийте чаю і дайте бажанню пройти. Старе поле нікого не вб’є за одну-дві ітерації. А ранній DROP COLUMN вбиває вихідні.

4. Міграція конфігурації

Конфігурація підступна тим, що її помилки рідко виглядають драматично: застосунок стартує, відповідає на health-check, проходить частину smoke-перевірок — і поводиться не так, як ви думаєте. Розбирайте її як окремий мініпроєкт, а не додаток до коміту.

У CashFlow Dashboard legacy-налаштування легко розповзаються по проєкту: application.yml, змінні середовища, старий прапор у CI, staging-конфіг, секретний шлях до зовнішнього сервісу. Після переходу на нову версію Boot частина ключів змінюється, частина застаріває, частина читається інакше.

Наприклад, замість розрізнених REPORT_TZ і USE_LEGACY_TZ розумніше зібрати одну точку правди:

cashflow:
  reports:
    timezone-source: utc-column      # тепер звіти читають новий стовпець
    default-zone: UTC                # єдина часова зона за замовчуванням
management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus  # перевіряємо після оновлення Boot

Головний принцип тут той самий, що й у даних: не стрибати різко. На один реліз тримають і старий ключ, і новий: застосунок шукає cashflow.reports.default-zone, немає його — тимчасово підхоплює legacy-ключ. Не красиво, зате практичний страховий варіант.

Є ще один нюанс: конфігурація — це не лише прикладні параметри, а й операційні: шляхи до логів, увімкнені endpoint’и, прапори dual-write, параметри планувальника, налаштування експортів, назви секретів. Не плутайте «задокументувати конфігурацію» і «розкрити секрети»: у POST_MIGRATION_NOTES.md вказують імена змінних, джерела та поведінку — не реальні токени й паролі.

Простіше всього думати так: компілятор бачить код, тести бачать поведінку, а конфігурацію в повному обсязі не бачить ніхто, крім вас і середовища. Тому inventory налаштувань, owner review і dry-run на staging тут означають не менше за тести в кодовій міграції.

5. Документація після міграції

Міграція закінчується не тоді, коли гілка злилася і CI зелений, а коли наступний інженер сам у всьому розбереться. Саме тому post-migration documentation — не бонус, а остання обов’язкова частина роботи.

Корисно бачити весь пакет артефактів як один ланцюжок:

RISK_MAP.md
   ↓
CHARACTERIZATION_TESTS.md
   ↓
COMPATIBILITY_MATRIX.md
   ↓
MIGRATION_PLAN.md → ROLLBACK.md → MIGRATION_VALIDATION_REPORT.md
   ↓
POST_MIGRATION_NOTES.md + оновлений runbook/операційна інструкція

POST_MIGRATION_NOTES.md не має повторювати MIGRATION_VALIDATION_REPORT.md рядок за рядком — його завдання інше: дати короткий, зрозумілий людині контекст. Зазвичай вистачає шести секцій:

Секція Навіщо потрібна
Що змінилося щоб через місяць не гадати, що саме мігрували
Як запускати щоб локальний і staging-запуск не залежали від пам’яті автора
Як перевіряли щоб validation evidence було пов’язано з зрозумілим сценарієм
Known issues щоб команда не сприймала обмеження за нові баги
Rollback path щоб відкат шукали в одному місці, а не по чату
Owners і follow-up cleanup щоб було ясно, хто спостерігає і що ще залишилося доробити

Шаблон початку такого документа може бути дуже простим:

# POST_MIGRATION_NOTES.md
Власник: @dashboard-tech-lead
Дата: 2026-05-24

Посилання:
- MIGRATION_PLAN.md
- ROLLBACK.md
- MIGRATION_VALIDATION_REPORT.md

## Що змінилося
## Як запускати та перевіряти
## Known issues і наступний cleanup

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

Окремо корисно оновити операційну інструкцію сервісу. Не обов’язково створювати новий файл, якщо в репозиторії вже є свій runbook або розділ у README для експлуатації. Але після міграції там мають зʼявитися щонайменше чотири речі: які метрики дивитися, який алерт вважати критичним, який прапор або налаштування використовується для швидкого відкату і хто чергує на цій ділянці. Це особливо важливо для конфігураційних міграцій: вони люблять ламатися не в момент релізу, а через кілька годин, коли вмикається реальне середовище і справжнє навантаження.

Гарний міграційний пакет залишає після себе дуже спокійне відчуття. Через пів року нова людина відкриває POST_MIGRATION_NOTES.md, бачить, що у звітах нормалізували час в UTC, читання перевели на normalized_ts_utc, dual-write вже вимкнено, перевірка описана в MIGRATION_VALIDATION_REPORT.md, відкат лежить у ROLLBACK.md, а за ділянку відповідає конкретний owner. Якщо такого відчуття немає і документ нагадує археологічну знахідку з написом «ну ми тут щось мігрували, здається, стало краще», значить робота ще не завершена.

1
Опитування
Migration execution: pilot slice та rollback , рівень 29, лекція 4
Недоступний
Migration execution: pilot slice та rollback
Migration execution: pilot slice та rollback
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ