JavaRush /Курсы /Claude code /Dependency graph и compatibility matrix

Dependency graph и compatibility matrix

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

1. Вы мигрируете не строку версии, а сеть зависимостей

Когда разработчик впервые слышит слова dependency graph, в голове иногда рисуется монструозная схема из сотни коробочек и стрелок, от которой хочется закрыть ноутбук и уйти выращивать помидоры. На практике всё гораздо прозаичнее. Граф нужен для одного: перестать гадать, что вы обновляете — одну библиотеку, цепочку библиотек или сборочный инструмент, от которого зависит всё остальное.

На примере CashFlow Dashboard это особенно заметно. Кажется, вы хотите «просто обновить Spring Boot». Но Boot не живёт в вакууме: он тянет Spring Framework, security-слой, Jackson, валидацию, часть тестовой инфраструктуры, конфигурационные ключи, иногда — поведение Gradle-плагина. Обновите одну строку версии в build.gradle, остальное сочтёте мелочью — и это уже не миграция, а техно-лотерея.

Здесь полезно различать два артефакта:

Артефакт На какой вопрос отвечает
Dependency graph Кто от кого зависит и через какую цепочку это приходит в проект
Compatibility matrix Что эта зависимость означает для миграции: безопасно ли обновлять, что сломается, нужен ли промежуточный шаг

Direct dependencies — это то, что вы явно написали в build.gradle. Transitive dependencies — это то, что пришло «в нагрузку». Если прямые зависимости — это гости, которых вы звали на вечеринку, то транзитивные — друзья гостей, которые пришли вместе с ними, а потом ещё привели своих знакомых. И обычно именно они создают самые интересные разговоры на кухне.

Например, прямые зависимости CashFlow Dashboard могут выглядеть вполне невинно:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.postgresql:postgresql'
}

Но если посмотреть чуть глубже, вы увидите уже цепочку:

+--- org.springframework.boot:spring-boot-starter-web -> 2.7.18
|    \--- com.fasterxml.jackson.core:jackson-databind -> 2.13.x
+--- org.springframework.boot:spring-boot-starter-security -> 2.7.18
|    \--- org.springframework.security:spring-security-config -> 5.7.x
\--- org.postgresql:postgresql -> 42.5.x

Вот здесь и начинается взрослая инженерия. Миграция ломается не на слове web starter как таковом, а на цепочке совместимости между Java, Gradle, Boot, Security, Jackson, JPA и вашим собственным кодом. Поэтому первый полезный вывод этой лекции звучит скучно, но очень практично: вы мигрируете не строку версии, а сеть зависимостей.

2. Сначала evidence, потом интерпретация

Когда граф уже получен, возникает второй вопрос: как его вообще читать, если вы не хотите провести вечер, глядя на тысячу строк вывода? Здесь помогает простое правило: не всё нужно анализировать одинаково глубоко. Сначала отделите важные узлы от фонового шума.

Для миграции CashFlow Dashboard я бы мысленно делил зависимости на три слоя. Платформенный: Java, Spring Boot, Gradle, Spring Security, JPA, драйвер БД. Инфраструктурный: JSON, логирование, миграции схемы, сериализация, интеграционные клиенты. Тестовый и служебный: JUnit, Mockito, test starters, плагины проверки. Новички часто смотрят только на runtime, забыв, что тесты и сборка тоже должны пережить миграцию, — а потом приложение «почти соберётся», а CI ляжет лицом вниз.

Чтобы не читать граф в сыром виде, полезно сохранять evidence отдельно: заведите каталог evidence/ и складывайте туда вывод зависимостей. Тогда Claude Code, вы и любой reviewer смотрят в один файл, а не пересказывают друг другу ощущения.

./gradlew dependencies > evidence/dependencies.txt
./gradlew dependencyInsight --dependency spring-security-config > evidence/security.txt

Ценна тут не команда, а привычка: сначала зафиксировать evidence, потом интерпретировать. С Claude Code это окупается сразу — вы даёте ему конкретный evidence/dependencies.txt, а не «ну там вроде много зависимостей, посмотри».

Ещё один важный момент для начинающих: не каждая строка графа обязана стать строкой матрицы — в матрицу попадает только та, что реально влияет на миграционное решение. Условный commons-lang3 живёт в проекте без приключений. А spring-security-config, jakarta.persistence, hibernate-core, postgresql driver, Gradle wrapper и Java toolchain почти наверняка заслуживают отдельной строки.

3. Sequence constraints — часть совместимости

Очень соблазнительно думать, что compatibility matrix — это просто список: компонент, версия, статус, риск. Но без зафиксированного порядка вы получите красивую таблицу, по которой всё равно нельзя безопасно двигаться. В миграции порядок важен почти как сами версии, иногда важнее: технически совместимые компоненты могут требовать строгой последовательности шагов.

На CashFlow Dashboard это видно особенно хорошо. Spring Boot 3.x требует более новую Java, новый Gradle wrapper — другой toolchain, а переход с javax.* на jakarta.* бессмысленно вносить, пока сборка живёт в старом окружении и не может это проверить. «Что обновлять» и «в каком порядке» — одна задача на двух уровнях.

Небольшая схема помогает это увидеть:

flowchart LR
    A[Migration Current State] --> B[Dependency graph]
    C[Official docs / release notes] --> D[Status and constraints]
    B --> D
    D --> E[COMPATIBILITY_MATRIX.md]

Внутри Status and constraints как раз живут sequence constraints. Например, у вас может быть такая логика:

Компонент Current Target Важное ограничение порядка
Java
8
21
Должна быть обновлена до перехода на Boot 3.x
Gradle
7.6.4
8.x
Нужен промежуточный шаг до нового Boot plugin
Spring Boot
2.7.18
3.x
Идёт после Java и обновления части build-окружения

Это не значит, что для каждого проекта порядок будет одинаковым, но для каждого проекта он должен быть явным. Если sequence constraint не записан, он всё равно существует — просто в голове автора. А всё, что живёт только в голове автора, в миграции обычно потом становится багом, который «почему-то никто не ожидал».

В матрице sequence constraint можно хранить прямо в строке компонента. Коротко и практично:

## Компонент
Gradle

## Текущая версия
7.6.4

## Целевая версия
8.x

## Статус совместимости
requires intermediate version

## Ограничение последовательности
Обновить до перехода на Boot 3.x и проверить toolchain под Java 21

Такая запись кажется избыточной ровно до того момента, пока кто-то не решит «сразу попробовать Boot 3, а Gradle потом». После этого избыточность резко превращается в спасательный круг.

4. Словарь статусов совместимости

Самая частая проблема плохих migration-артефактов — расплывчатый язык. «Надо проверить», «вроде совместимо», «может потребоваться адаптация», «посмотрим по ходу». Это всё звучит вежливо, но не помогает принимать инженерные решения. Compatibility matrix хороша только тогда, когда в ней есть фиксированный словарь статусов. Иначе у каждого автора будет свой диалект, а у reviewer'а — головная боль.

И здесь полезно помнить разницу между двумя слоями. confirmed / assumption / unknown живут в research notes и показывают, насколько мы уверены в выводе. Матрица отвечает на другой вопрос: что с этим делать в плане миграции. Поэтому незакрытый unknown не становится магически решением — обычно он приезжает сюда как needs manual validation, а иногда доживает до Open risks в MIGRATION_PLAN.md.

Для этого уровня полезно держаться одного набора статусов:

Статус Что означает на практике
safe update
Обновление выглядит прямым и не требует заметных кодовых изменений
requires intermediate version
Нельзя прыгнуть сразу; нужен промежуточный шаг
blocked
Сейчас переход заблокирован несовместимостью или отсутствием условий
needs replacement
Компонент проще заменить, чем тянуть дальше
needs code changes
Код проекта должен быть адаптирован под новую версию
needs config changes
Потребуются изменения конфигурации, пропертей, build-настроек
needs manual validation
По коду и docs нельзя честно подтвердить совместимость без ручной проверки

Для начинающих особенно полезен последний статус. Он очень дисциплинирует. Вместо того чтобы притворяться уверенным, когда evidence не хватает, вы честно пишете needs manual validation. Это не слабость, а признак нормальной инженерной работы. Слабость — написать safe update, потому что так спокойнее смотрится таблица.

На практике один компонент иногда требует двух отметок сразу. Например, Spring Boot 2.73.x для CashFlow Dashboard почти наверняка будет означать и needs code changes, и needs config changes. В таком случае лучше выбрать один основной статус, а второй явно описать в Required action. Но если вашей команде удобнее писать двойной статус, вроде needs code changes + needs config changes, это тоже допустимо — главное, чтобы правило было единым по всей матрице.

Вот хороший пример строки, которая не прячется за туманом формулировок:

## Компонент
Spring Boot

## Текущая версия
2.7.18

## Целевая версия
3.x

## Статус совместимости
needs code changes + needs config changes

По такой строке уже понятно, что это не «обновили и забыли». А дальше reviewer идёт в Required action и Evidence, где видит уже конкретику.

5. Читабельный COMPATIBILITY_MATRIX.md

Хорошая матрица совместимости — это не огромная Excel-таблица из ада и не роман в двенадцати томах. Это повторяемый, читаемый артефакт, где у каждой строки один и тот же каркас. Важна не форма ради формы, а то, что по ней легко пройти глазами и быстро увидеть: где безопасно, где риск, где blocker, где недостаточно evidence.

Для CashFlow Dashboard я бы рекомендовал такой шаблон строки:

## Компонент
...

## Текущая версия
...

## Целевая версия
...

## Статус совместимости
...

## Риск
...

## Требуемое действие
...

## Ограничение последовательности
...

## Доказательства
...

Обратите внимание на поле Evidence. Именно оно отличает матрицу от набора умных мыслей. В Evidence должны лежать ссылки на реальные источники: build.gradle, gradle-wrapper.properties, вывод dependencies, конкретный класс в коде, раздел migration guide, release notes. Если evidence нет, у вас не строка матрицы, а заготовка для обсуждения.

Ниже — три коротких примера, которые хорошо показывают разницу между типами строк.

## Компонент
Spring Boot
## Текущая версия
2.7.18
## Целевая версия
3.x
## Статус совместимости
needs code changes
## Доказательства
build.gradle; migration guide: Jakarta EE 9

Здесь строка сразу говорит: обновление упрётся не только в версию, но и в код.

## Компонент
Gradle
## Текущая версия
7.6.4
## Целевая версия
8.x
## Статус совместимости
requires intermediate version
## Доказательства
gradle-wrapper.properties; Gradle release notes

А здесь уже видно, что нельзя перепрыгнуть шаг «потому что хочется быстрее».

## Компонент
Custom Hibernate UserType
## Текущая версия
legacy implementation
## Целевая версия
jakarta-compatible implementation
## Статус совместимости
needs manual validation
## Доказательства
src/main/java/.../MoneyUserType.java; docs insufficient

Это отличный пример честной строки. Код есть, проблема видна, но официальный источник не даёт гарантии, что всё переживёт миграцию без ручной проверки. Значит, так и пишем.

Ещё одна полезная привычка: включать в матрицу не только библиотеки, но и системные компоненты среды. Java — это компонент. Gradle wrapper — компонент. Spring Boot plugin — компонент. PostgreSQL driver — компонент. Иногда даже тестовый стек — отдельный компонент. Потому что миграция ломается не только в runtime-коде, а в любой точке, где сходятся версия, toolchain и ваша реальная кодовая база.

6. Claude Code как аналитик, а не гадалка

Когда у вас уже есть current-state inventory, evidence-файлы и заметки из official docs, Claude Code становится очень полезным помощником. Но именно помощником-аналитиком, а не автоматическим мигратором. Его задача здесь — читать фиксированные входы, помогать группировать зависимости, предлагать строки матрицы и показывать, где evidence не хватает. Не больше. Как только он начинает в духе «ну обычно тут всё совместимо», пора возвращать разговор на землю.

Хороший запрос к Claude Code в этой теме звучит примерно так:

Построй строки compatibility matrix для CashFlow Dashboard.
Используй только:
- build.gradle
- gradle-wrapper.properties
- evidence/dependencies.txt
- migration research notes
Для каждой строки верни:
component, current, target, status, risk, sequence constraint, evidence.
Если подтверждения не хватает, ставь needs manual validation.
Ничего не обновляй и не запускай.

Заметьте, здесь есть три важных ограничения. Во-первых, вы задаёте фиксированные входы. Во-вторых, вы задаёте фиксированный словарь полей. В-третьих, вы прямо запрещаете execution. Это сильно снижает шанс, что Claude уйдёт в жанр «сейчас я вам ещё и полпроекта обновлю, раз уж открыл терминал».

При этом финальное решение по каждой строке всё равно остаётся за вами. Claude может помочь быстро заметить, что spring-boot-starter-security тянет старую ветку security-конфигурации, или что кастомный MoneyUserType не имеет явной совместимости с jakarta-миром. Но статусы blocked, needs replacement и даже safe update должен утверждать человек, который прочитал evidence и понимает риск для системы.

Полезная практическая проверка очень проста. Откройте любую строку своей матрицы и попробуйте вслух ответить на три вопроса: что это за компонент, почему у него такой статус и на какой источник вы опираетесь. Если на одном из вопросов вы начинаете говорить что-то вроде «ну это Claude предположил», строка ещё не готова. А если каждая строка читается так же уверенно, как комментарий к обычному PR, значит COMPATIBILITY_MATRIX.md действительно выполняет свою работу: показывает не только список зависимостей, а реальную карту риска и совместимости для миграции CashFlow Dashboard.

1
Задача
Claude code, 28 уровень, 3 лекция
Недоступна
Сборка COMPATIBILITY_MATRIX.md
Сборка COMPATIBILITY_MATRIX.md
1
Задача
Claude code, 28 уровень, 3 лекция
Недоступна
Фиксация динамической версии в compatibility matrix
Фиксация динамической версии в compatibility matrix
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ