1. Память ИИ в миграции — плохой штурман
Когда вы работаете с обычной задачей в знакомом коде, память ИИ часто выручает: напомнит синтаксис, подкинет шаблон теста. Миграция — другая территория: важны не общие паттерны, а точные изменения между конкретными версиями. И тут память становится не источником истины, а источником соблазна.
Проблема в том, что модель помнит усреднённую картину: смешает советы для Spring Boot 3.0, 3.1 и 3.3 в один красивый рассказ, выдаст правдоподобное «обычно это решается так» — а ваш проект как раз тот случай, где «обычно» не работает. Она не злонамеренна. Просто очень хороший собеседник, а не официальный changelog.
Поэтому в миграции действует жёсткое правило: память ИИ — это старт гипотезы, а не доказательство. Claude говорит «в Boot 3 надо заменить javax на jakarta» — не готовая работа, а повод открыть migration guide и проверить, где проект попадает под изменение.
Сравните два запроса. Первый удобен, но опасен:
Расскажи, что ломается в Spring Boot 3,
и как нам быстро починить проект.
Второй похож на инженерную постановку:
Сравни наш текущий стек Boot 2.7.18 + Java 8
с target-стеком Boot 3.x + Java 21.
Используй только official migration guide и release notes.
Для каждого вывода укажи docs section и file path в repo.
Неподтверждённые места помечай как Unknown.
Во втором варианте вы не просите модель «быть умной» — вы просите её работать как аналитик. Это совсем другой режим. И именно он нужен нам в CashFlow Dashboard, где цена ошибки — не только красный build, но и тихо сломанный расчёт подписок.
2. Авторитетный источник, а не подсказка
Фраза authoritative source звучит солидно, а смысл у неё простой: источник, которому доверяют первым. Не единственный — первым. Технология принадлежит вендору — его guide, release notes и changelog стоят выше блогов, форумов и чужих пересказов.
Для миграции CashFlow этого особенно нельзя забывать, потому что меняется сразу несколько слоёв: Spring Boot, Java, Gradle, база данных. Один блог-пост не заменит четыре независимых журнала изменений.
| Источник | На какой вопрос отвечает | Как использовать |
|---|---|---|
| Файлы проекта и Migration Current State | Что у нас есть сейчас | Это точка старта: версии, конфиги, API, CI, кастомные адаптеры |
| Official migration guide | Что вендор считает обязательными изменениями | Читаем как карту перехода между версиями |
| Release notes / changelog | Что именно менялось от релиза к релизу | Нужны, чтобы не пропустить накопленные изменения между minor-версиями |
| Build/test output | Что реально ломается в вашем проекте | Это окончательный практический судья, но не на сегодняшнем этапе |
| Блоги, форумы, ответы на StackOverflow | Как другие люди объясняют проблему | Полезны только после официальной документации, как вторичный слой |
Обратите внимание на одну деталь. Сегодня мы ещё не запускаем целевой build и не собираем runtime evidence. И даже идеальный guide не докажет совместимость вашего кастомного JsonAdapter или старого UserType: документация показывает, где искать риск, но не обещает, что проект поведёт себя по учебнику. Проекты вообще редко читают учебники. Документация отвечает на «что должно измениться по правилам технологии», код — на «где это нас заденет»; полезное исследование дают оба ответа.
3. Как выглядит нормальный changelog-driven research
Услышав «читайте release notes», вы можете воспринять это как наказание за прошлые грехи. Но в правильном порядке они из бюрократии превращаются в рабочий инструмент: не читать всё подряд, а идти от текущего состояния к целевому стеку.
Нормальный цикл выглядит так:
flowchart TD
A[Migration Current State] --> B[Official migration guide]
B --> C[Release notes и changelog]
C --> D[Сопоставление с repo]
D --> E[Статусы: confirmed / assumption / unknown]
E --> F[Research notes по миграции]
Сначала вы берёте уже собранный Migration Current State и фиксируете, откуда и куда идёте: для нас это Boot 2.7.18 → Boot 3.x и Java 8 → Java 21. Открываете guide именно для этого перехода. Читаете release notes промежуточных релизов — изменения размазаны по цепочке 3.0 → 3.1 → 3.2 → 3.3, а не собраны на одной странице.
Только затем начинается сопоставление с кодом. Не наоборот. Сначала понимание, что именно вы ищете, иначе grep выдаст классику: двадцать импортов, десять конфигов, пять тревожных мест и ноль понимания, какие из них важны.
Запрос здесь нужен не креативный, а дисциплинированный:
Используй official migration guide и release notes
для Boot 3.x и Java 21.
Сравни их с файлами build.gradle, application.yml,
gradle-wrapper.properties и src/main/java.
Верни только релевантные breaking changes,
затронутые файлы и места для ручной проверки.
Для каждого пункта укажи docs section и file path.
Не предлагай правки кода и не запускай build.
Обратите внимание на две вещи: вы ограничили набор файлов и запретили ранние действия — иначе исследование сползёт в «я уже всё понял, давайте сразу чинить». Нет, не давайте. Сегодня мы собираем доказательства. Код ещё успеет пострадать без нас.
4. Привязка breaking changes к коду CashFlow
Вот где начинается настоящая инженерная работа: построить мост между внешним источником и кодом CashFlow. Пока вы не показали пальцем — «этот пункт guide касается вот этого файла» — документация остаётся красивой пачкой слов.
Начинать удобно с формальных мест — с build.gradle:
plugins {
id 'org.springframework.boot' version '2.7.18'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
}
Это маленький фрагмент, но он уже многое говорит: эти две строки фиксируют стартовую площадку. Guide требует Java 17+ — вы уже знаете, где первое прямое несоответствие.
Дальше — кодовые сигналы. Классический маркер миграции на современный Spring — старые javax.* импорты:
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Subscription {
@Id
private Long id;
}
Есть в документации раздел про переход на jakarta.* — и этот код становится подтверждённой точкой соприкосновения guide и проекта. Не гипотеза. Факт: изменение касается этого файла.
Так же работает старый security-конфиг:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
}
Даже если вы ещё не знаете новый стиль конфигурации, риск вы уже видите. Не потому, что «Claude где-то слышал», а потому, что guide говорит про изменения в Spring Security, а код использует устаревший подход.
Такие соответствия удобно держать в компактной таблице:
| Что нашли в официальных docs | Что ищем в проекте | Где это видно в CashFlow |
|---|---|---|
| Переход javax.* → jakarta.* | Импорты javax.persistence, javax.validation | Файлы: Subscription.java, BillingHistory.java |
| Новый baseline Java | sourceCompatibility, CI-образ, toolchain | Файлы: build.gradle, CI workflow |
| Изменения в Spring Security | WebSecurityConfigurerAdapter, старые DSL-цепочки | Файл: SecurityConfig.java |
| Конфигурационные изменения | Старые ключи в application.yml | Файл: src/main/resources/application.yml |
| Возможные проблемы с кастомными типами | Самописные адаптеры и ORM-расширения | Типы: JsonAdapter, MoneyUserType |
Последняя строка особенно интересна. Она показывает, что не всё в исследовании обязано сразу стать подтверждённым фактом. Кастомный MoneyUserType может не иметь прямого описания в guide. Значит, он не превращается магически в «всё будет хорошо». Он уходит в зону Unknown и ждёт ручной проверки. Это как раз зрелое поведение, а не слабость. Слабость — это закрыть его фразой «наверное, совместимо».
5. Accelerators: docs lookup без режима оракула
Когда исследование становится длинным, появляется естественное желание ускориться. И это нормальное желание. В конце концов, мы пришли на курс по Claude Code не для того, чтобы вручную листать сто страниц документации, изображая монаха-схоласта. Но ускорение должно быть управляемым. Иначе вы просто замените слепую веру в память ИИ слепой верой в красивый инструмент.
Хороший docs lookup работает как хороший библиотекарь: быстро приносит нужную полку. Плохой — как знакомый, вбегающий с криком: «Я всё понял, давайте срочно переписывать!» В миграции второй тип особенно опасен.
Если у команды есть read-only MCP-сервер для поиска по документации, его конфиг может выглядеть примерно так:
mcpServers:
spring-docs:
command: npx
args: ["-y", "@example/spring-docs-mcp"]
permissions: read-only
Важна не марка пакета — имя сервера, плагина или утилиты у вас может быть другим. Стабильный принцип один: доступ только на чтение. Инструмент ищет и цитирует, а не меняет код, запускает recipe и «завершает миграцию за вас».
Хорошо, если границы зафиксированы и в CLAUDE.md migration-сессии:
## Режим исследования миграции
- source of truth: official docs + repo files
- каждый вывод = docs section + file path
- неподтверждённое помечать Unknown
- не предлагать edit, build и version bump без запроса
Это очень полезная маленькая дисциплина: она не даёт сессии уплыть в «ну я и так уже всё понял» — уплывает она ровно тогда, когда вы расслабились и решили, что проверять больше не нужно.
Если у вас есть Context7-like workflow, docs plugin или свой migration-assistant из Workflow Kit — относитесь к ним так же. Они ускоряют путь к разделу, собирают выжимку, подсвечивают файлы. Но вердикт «это confirmed» вы выносите, только увидев docs section и file path рядом, в одном кадре. Остальное — красивые рассказы.
6. Фиксация выводов: confirmed, assumption, unknown
Самое взрослое сегодня — даже не источники, а язык, которым вы фиксируете результат. Нет этого языка — команда путает факт, догадку и надежду. А в миграции надежда — плохой dependency manager.
Поэтому удобно использовать три статуса:
| Статус | Что означает | Как с ним обращаться |
|---|---|---|
| confirmed | Есть подтверждение и в docs, и в repo | Можно включать в рабочие решения |
| assumption | Есть сильная гипотеза, но не хватает части доказательств | Нельзя считать завершённым выводом |
| unknown | Ни docs, ни код не дают честного ответа без проверки | Явно выносится в ручную верификацию |
Здесь важно не смешивать этот словарь со словарём COMPATIBILITY_MATRIX.md. confirmed / assumption / unknown описывают качество знания на этапе research notes. Чуть дальше появятся статусы решений вроде needs code changes или needs manual validation. Если assumption или unknown так и не получили подтверждения, они не исчезают: в матрице становятся needs manual validation, а в плане доживают до Open risks.
Это не бюрократия, а способ не врать самому себе. Например, так может выглядеть рабочий фрагмент заметок внутри документа исследования:
## Миграция Jakarta
- confirmed: `javax.persistence` используется в `Subscription.java`
и `BillingHistory.java`; см. раздел migration guide про Jakarta.
- assumption: автоматическая массовая замена импортов не затронет
кастомные аннотации в модуле billing.
- unknown: совместимость `MoneyUserType` с новым стеком ORM.
Здесь всё устроено правильно: третий пункт нельзя «закрыть оптимизмом» — он честно висит незавершённым вопросом. И это хорошо: Unknown не признак слабости, а признак того, что вы ещё не начали играть в рулетку.
Если вынесете из лекции одну привычку — пусть будет эта: не заполнять пробелы уверенным тоном. В миграции тон голоса не доказывает ничего; доказывают официальная документация, конкретный файл в репозитории и честная пометка, где знание заканчивается. В тот момент, когда вы спокойно пишете Unknown вместо «наверное, нормально», исследование перестаёт быть гаданием и становится инженерной работой.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ