Сборка и зависимости

Что это такое

Проект на Java собирает инструмент: он компилирует исходники, разрешает зависимости, прогоняет тесты и упаковывает результат, а решатель зависимостей скачивает библиотеки из репозитория, разрешает их собственные зависимости транзитивно и помещает весь набор в classpath. Описание сборки лежит в репозитории, поэтому процесс воспроизводим, а набор зависимостей доступен для ревью.

Почему это важно

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

Ключевые идеи

Сборка — это граф задач с порядком зависимостей

Компиляция зависит от разрешения зависимостей, тесты зависят от компиляции, упаковка зависит от тестов, и порядок инструмент вычисляет сам. Объявление задач, а не последовательности команд оболочки, делает возможной инкрементальную сборку: повторяются только задачи, у которых изменились входные данные; одно и то же описание даёт одни и те же шаги, поэтому проверка чистой сборки осмысленна.

разрешение зависимостей -> компиляция -> тесты -> упаковка -> публикация

Транзитивные зависимости — обычный источник конфликтов

Библиотека объявляет свои требования, и когда запрошено несколько версий, решатель выбирает одну. Какая победит, зависит от стратегии разрешения, а ответ влияет на поведение, а не только на компиляцию. Стандартный способ разобраться — посмотреть разрешённое дерево и осознанно исключить или зафиксировать версию.

Работает classpath, а не путь компиляции

Библиотека, нужная только для компиляции и отсутствующая во время работы, даёт ошибку при первом выполнении кода, который ею пользуется. Различать эти две области видимости — настоящее умение: зависимость только для компиляции служит обработчиком аннотаций или заглушкой API, а всё остальное должно присутствовать при запуске программы.

Фиксация версий делает сборки воспроизводимыми

Разрешение до последней совместимой версии означает, что сегодняшняя сборка отличается от вчерашней без единого изменения в исходниках. Фиксация прямых зависимостей и закрепление транзитивного набора там, где инструмент это поддерживает, делают артефакт воспроизводимым, а обновление — осознанным действием, а не случайностью.

Артефакты публикуются и потребляются по координатам

Библиотека опознаётся по группе, имени и версии и публикуется в репозиторий, который читают и локальные проекты, и удалённые. Эта идентичность делает объявление зависимости однозначным, а приватный репозиторий — местом для внутреннего кода. Версия-снимок по определению изменчива, и продакшен не должен от неё зависеть.

Воспроизводимая сборка включает инструментарий

Версия самого инструмента сборки, уровень языка и целевая среда выполнения влияют на артефакт. Их объявление в описании сборки убирает ещё один источник фразы «на сборочной машине собралось иначе»; в идеале это делают через обёртку, которая использует точную версию инструмента.

Практика

Посмотрите разрешённое дерево зависимостей

Запустите отчёт инструмента сборки о зависимостях для небольшого проекта и найдите путь, по которому пришла транзитивная зависимость.

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

Зафиксируйте и воспроизведите сборку

Соберите проект дважды из чистого состояния с зафиксированными версиями и сравните полученные артефакты.

Готово, когда: обе сборки разрешаются в одни и те же версии и дают артефакт с одинаковым содержимым.

Частые ошибки

  • Динамическое разрешение зависимостей — сборка меняется без изменений в исходниках; исправить: фиксируйте версии и закрепляйте транзитивный набор.
  • Зависимость только для компиляции в рабочем classpath — её поставляют без причины, либо нужной только во время работы не хватает и она отказывает позже; исправить: держите области видимости осознанными.
  • Игнорирование разрешённого дерева — две библиотеки борются за третью, и победитель случаен; исправить: смотрите дерево и явно фиксируйте или исключайте.
  • Зависимость от версии-снимка в продакшене — артефакт меняется под тем же именем; исправить: зависьте от выпущенных версий.
  • Опора на версию инструмента, установленную у разработчика — сборка отличается от машины к машине; исправить: используйте обёртку, которая фиксирует инструмент сборки.

Что читать дальше