Dependency hell (англ. ад зависимостей) — это антипаттерн управления конфигурацией, разрастание графа взаимных зависимостей программных продуктов и библиотек, приводящее к сложности установки новых и удаления старых продуктов. В сложных случаях различные установленные программные продукты требуют наличия разных версий одной и той же библиотеки. В наиболее сложных случаях один продукт может косвенно потребовать сразу две версии одной и той же библиотеки.[1] Проблемы с зависимостями возникают у общих пакетов/библиотек, у которых некоторые другие пакеты имеют зависимости от несовместимых и различных версий общих пакетов. Если установлена одна версия общего пакета/библиотеки, для решения этой проблемы автоматизатору тестирования/программисту/администратору понадобится получить новые или старые версии зависимых пакетов. Это, в свою очередь, может нарушить работу других зависимых пакетов и добавить проблем в другой набор пакетов, таким образом образуя настоящий ад.
Обычно, вместо того, чтобы «изобретать велосипед», ПО разрабатывается так, чтобы получать необходимые функции из других программных компонентов которые доступны разработчику или были разработаны в другом месте. Это можно сравнить с тем, как люди, которые строят дом, могут купить готовые материалы, такие как кирпичи, окна и двери, нежели создавать их самостоятельно.
Даже для строителя может оказаться проблемой, если строение сделано для определенного типа дверей, и доступны только двери с разными спецификациями. Однако в мире программного обеспечения, где компоненты развиваются очень быстро и сильно зависят друг от друга, эта проблема становится намного актуальнее.
Проблему «ада зависимостей» можно рассматривать как антипаттерн, где вина лежит не столько на поставщиках продукта, сколько на фреймворке, в рамки которого они должны входить.
«Dependency hell» принимает несколько форм[2]:
Приложение зависит от большого числа объёмных библиотек, которые требуют длительных скачиваний, занимают много дискового пространства. Возможна ситуация, когда приложение построено на определённой платформе (например Java) и требует установки этой платформы, в то время, как 99% остальных ваших приложений поддержки этой платформы не требуют. Это частный случай проблемы когда либо приложение использует маленькую часть большой платформы и в конечном итоге требует установки всей платформы (что может быть решено только с помощью рефакторинга приложения), либо маленькое приложение опирается на большое число различных библиотек одновременно.
Приложение зависит от библиотеки "А", которая зависит от библиотеки "Б", ... , которая в свою очередь зависит от библиотеки "Я". Это частный случай проблемы множество зависимостей, с той разницей, что большое количество зависимостей осложнено их запутанной и длительной взаимосвязью, которая должна решаться вручную. (Пользователь устанавливает приложение, но ему выдаётся требование установить библиотеку "А", он устанавливает библиотеку "А", но при попытке это сделать библиотека "А" запрашивает установку библиотеки "Б" и т.д.
Иногда наличие такой длинной цепочки зависимостей может приводить к возникновению конфликтов, когда разными компонентами цепочки требуются разные версии одного и того же пакета или библиотеки. (см. конфликтующие зависимости) Такие длинные цепочки зависимостей должны решаться пакетными менеджерами, которые делают это в автоматическом режиме, вместо того чтобы заставлять пользователя решать их ручным способом, при котором зависимости могут остаться частично неудовлетворёнными (не все инсталляторы библиотек постоянно напоминают о всех своих зависимостях пользователю).
Если "Приложение 1" зависит от библиотеки "А" версии 1.2, а "Приложение 2" зависит от той же библиотеки "А", но уже версии 1.3, и различные версии библиотеки "А" не могут быть одновременно установлены, то "Приложение 1" и "Приложение 2" нельзя одновременно использовать (или даже установить, если установщики проверяют зависимости).
Когда возможно одновременное использование разных версий библиотеки — это решают путём параллельных установок разных версий библиотеки. В противном же случае, установка с использованием новой версии библиотеки должна сопровождаться удалением старой версии библиотеки и, соответственно, всех программ, которые зависят от неё т.к. они будут неработоспособны.
На Linux-системах эта проблема часто возникает при попытке установки в систему пакетов от разных разработчиков, которые не предназначены для этой версии системы. В таком случае удовлетворение долгой цепи зависимостей пакетов даже может привести, например, к запросу другой версии библиотеки glibc, одной из крайне важных, основополагающих системных библиотек. Если это случается пользователю будет предложено удалить тысячи пакетов, что по сути будет равноценно удалению, например, 80% системы, включая графические оболочки и сотни различных программ.
Ситуация, когда приложение "А" версии 2 зависит от приложения "Б", которое зависит от приложения "В", которое в свою очередь зависит от приложения "А" , но версии 1. Это приводит к тому, что в пакетных системах типа RPM или DPKG, пользователь должен установить две версии одного и того же исходного приложения "А", что может оказаться недопустимо и не разрешено менеджером пакетов. Однако на Linux-системах наличие циклической зависимости, чаще всего является результатом непонимания пользователем того, как пользоваться операционной системой и её менеджером пакетов.
Наиболее очевидное (и часто используемое) решение проблемы — стандартизированная нумерация версий, при которой в программном обеспечении используется специфический номер для каждой версии (aka основная версия), а также дополнительное число для второстепенной версии (aka minor version), например: 10.1, или 5.7. Основная версия изменяется, только, когда программа, которая имеет эту версию, больше не будет совместима с обновленной программой с учётом внесенных в неё изменений. Вспомогательная версия может изменяться даже при небольших изменениях в коде, которые не блокируют стороннему программному обеспечению возможность работы с разрабатываемой программой. В случаях, таких как этот, сторонние программы могут просто запрашивать компонент, имеющий определенную основную версию, и произвольную младшую, второстепенную (больше либо равную определенной минорной версии). Все будет продолжать работать, и зависимости будут разрешены успешно, даже, если второстепенная версия изменилась.
Решение с нумерацией версий можно улучшить, сделав нумерацию версий поддерживаемой на уровне операционной системы. Это позволит приложению запрашивать модуль/библиотеку по уникальному названию и задавать ограничения на номера версий, эффективно используя операционную систему. Общий модуль может быть помещен в центральное хранилище без риска отказа приложений, которые зависят от предыдущих или последующих версий этого модуля. Каждая версия получает свою собственную запись в хранилище, находясь рядом с другими версиями того же самого модуля. Такое решение используется в операционной системе Windows начиная с Windows Vista, где Global Assembly Cache является имплементацией такого центрального хранилища со связанными сервисами и интегрированным менеджером пакетов.
Пакетные менеджеры с программным управлением могут выполнять обновления независимых программных компонентов, одновременно разрешая также несовместимость, связанную с основным номером версии.
Многие современные дистрибутивы Linux имеют пакетные менеджеры, основанные на хранилищах для решения проблемы зависимостей. Эти системы — слой поверх менеджеров пакетов RPM, dpkg, или других пакетных систем, и спроектированы для автоматического разрешения зависимостей путём поиска в предопределенном хранилище программного обеспечения. Обычно эти хранилища программного обеспечения представляют из себя FTP или web-сайты, каталоги на локальном компьютере или распределенные через компьютерные сети или, что встречается менее часто, каталоги на съёмных носителях, таких как CD или DVD. Это исключает ад зависимостей для пакетов программного обеспечения, хранящихся в тех репозиториях, которые обычно поддерживаются провайдерами дистрибутивов Linux и на зеркалах по всему миру. Также эти репозитории часто велики, невозможно иметь все части программного обеспечения в них сразу, поэтому ад зависимостей всё же может наступить. В любом случае, специалисты по обслуживанию репозиториев сталкиваются с адом зависимостей в той или иной мере.[3] Примерами таких систем являются APT, YUM, urpmi, Zypper, Portage, Pacman и другие.
PC-BSD (операционная система на базе FreeBSD) до версии 8.2 справляется с dependency hell путём размещения пакетов и зависимостей в самодостаточные каталоги-контейнеры, избегая таким образом повреждения системных библиотек при обновлениях или иных их изменениях. Система PC-BSD использует «PBI» (Push Button Installer) как основной пакетный менеджер.[4]
Поскольку у различных частей программного обеспечения есть различные зависимости, возможно войти в порочный круг требований к зависимостям, или (что возможно хуже) постоянно расширяющееся дерево требований, поскольку каждый новый пакет требует, чтобы еще несколько были установлены. Системы, такие как APT, могут разрешать это, предоставляя пользователю ряд решений для выбора и позволяя ему принять или отклонить их по его желанию.
Если прикладное программное обеспечение спроектировано таким способом, что его разработчики в состоянии легко адаптировать интерфейс, который имеет дело с операционной системой, менеджером окон или средой рабочего стола к новым или изменяющимся стандартам, то программисты должны были бы только контролировать уведомления от создателей среды или проектировщиков библиотек компонент и быстро приспособлять обновления их программного обеспечения к обновлениям для пользователей, затраты были бы минимальными и отнимающая много времени дорогостоящая модернизации не была бы необходима. Этот метод поощрил бы программистов активно взаимодействовать с теми, от кого они зависят, чтобы поддержать разумный процесс уведомлений, который не обременяет никого вовлеченного.
Другой подход к предотвращению проблем зависимостей состоит в развертывании программного обеспечения через программно-аппаратный комплекс. Зависимости инкапсулируются в модуль, позволяя пользователям не беспокоиться о зависимостях в программном обеспечении. Это — забота разработчиков программного обеспечения.
В данном случае подразумеваются приложения (либо версии стандартных приложений), работающие в собственной, замкнутой и самодостаточной, среде и имеющую минимальные зависимости от системных библиотек. Все требуемые для работы программы компоненты добавляются на этапе собственной разработки, кодирования и пакетирования, при этом важные для работы программы файлы и компоненты максимально инкапсулируются в независимой от остальной системы среде, в результате, путём минимального воздействия с остальной системой, удаётся избежать большинства проблем с неразрешёнными зависимостями. Зачастую может работать независимо от системы, на которой приложение запущено. Приложения в RISC OS и ROX Desktop в среде Linux используют application directories, таким образом работая по схожему принципу: программа со всеми своими зависимостями содержится в собственном самодостаточном каталоге (папке).[5]
На определенных программных платформах, понятие «dependency hell» получает своё собственное название, в зависимости от названия конфликтующих компонентов.
Данная страница на сайте WikiSort.ru содержит текст со страницы сайта "Википедия".
Если Вы хотите её отредактировать, то можете сделать это на странице редактирования в Википедии.
Если сделанные Вами правки не будут кем-нибудь удалены, то через несколько дней они появятся на сайте WikiSort.ru .