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

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

Claude code
29 уровень , 4 лекция
Открыта

1. Данные и конфигурация — отдельный класс риска

На этой теме многие впервые понимают, почему миграции данных и конфигурации пугают даже сильных разработчиков. Код откатывается коммитом, а проблему видно глазами компилятора. Данные и настройки ломаются молча, без спецэффектов, но с дорогими последствиями. Это отдельный класс риска.

И здесь важно не склеить этот разговор с первым migration/boot3-pilot на reports/*. Там был маленький read-only Boot 3 pilot без изменений схемы и конфигурационных прыжков. Здесь пример другой — со своей веткой, 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. Post-migration documentation

Миграция заканчивается не тогда, когда ветка слилась и 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
Owner: @dashboard-tech-lead
Date: 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
Задача
Claude code, 29 уровень, 4 лекция
Недоступна
Фазовый план миграции данных expand → backfill → switch → contract
Фазовый план миграции данных expand → backfill → switch → contract
1
Задача
Claude code, 29 уровень, 4 лекция
Недоступна
Подготовка post-migration documentation
Подготовка post-migration documentation
1
Опрос
Migration execution: pilot slice и rollback , 29 уровень, 4 лекция
Недоступен
Migration execution: pilot slice и rollback
Migration execution: pilot slice и rollback
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ