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[Baseline на старом стеке] --> B[Те же проверки на новом стеке]
    B --> C{Поведение совпало?}
    C -- да --> D[Go или Continue pilot]
    C -- частично --> E[Исправить и повторить проверку]
    C -- нет --> F[No-go или Rollback]

Если вы держите эту схему в голове, validation перестаёт быть охотой на баги и становится внятным сравнительным экспериментом.

3. Цепочка проверок: датчики с доказательной силой

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

Проверка На какой вопрос отвечает Чем подтверждаем
build
проект вообще собирается на новом стеке? лог сборки
targeted tests не сломалась ли локальная бизнес-логика в 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, одни и те же проверки до и после, цепочка документов, ответственный, формальное решение. А значит, есть главное, ради чего стоит заниматься миграциями профессионально: не только движение вперёд, но и доказательство, что вы не сломали то, что обещали сохранить.

1
Задача
Claude code, 29 уровень, 2 лекция
Недоступна
Contract test для feature parity
Contract test для feature parity
1
Задача
Claude code, 29 уровень, 2 лекция
Недоступна
Подготовка MIGRATION_VALIDATION_REPORT.md
Подготовка MIGRATION_VALIDATION_REPORT.md
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ