Dependency hell es un antipatrón de gestión de configuración , el crecimiento de un gráfico de dependencias mutuas de productos de software y bibliotecas , lo que dificulta la instalación de productos nuevos y la eliminación de productos antiguos. En casos complejos, diferentes productos de software instalados requieren diferentes versiones de la misma biblioteca. En los casos más complejos, un producto puede requerir indirectamente dos versiones de la misma biblioteca a la vez. [1] Los problemas de dependencia ocurren con paquetes/bibliotecas compartidos donde algunos otros paquetes tienen dependencias en versiones incompatibles y diferentes de los paquetes compartidos. Si se instala una versión de un paquete / biblioteca compartidos, el automatizador / programador / administrador de pruebas deberá obtener versiones nuevas o antiguas de los paquetes dependientes para resolver este problema . Esto, a su vez, puede romper otros paquetes dependientes y agregar problemas a otro conjunto de paquetes, creando así un verdadero infierno.
Por lo general, en lugar de "reinventar la rueda", el software está diseñado para obtener la funcionalidad necesaria de otros componentes de software que están disponibles para el desarrollador o que se han desarrollado en otro lugar. Esto se puede comparar con la forma en que las personas que construyen una casa pueden comprar materiales listos para usar, como ladrillos, ventanas y puertas, en lugar de crearlos ellos mismos.
Incluso para el constructor, puede ser un problema si el edificio está hecho para cierto tipo de puerta y solo hay disponibles puertas con diferentes especificaciones. Sin embargo, en el mundo del software, donde los componentes evolucionan muy rápido y dependen mucho unos de otros, este problema cobra mucha más relevancia.
El problema del "infierno de la dependencia" puede verse como un anti- patrón , donde la culpa no es tanto de los vendedores del producto como del marco en el que deberían estar incluidos.
El "infierno de la dependencia" toma varias formas [2] :
La aplicación depende de una gran cantidad de bibliotecas grandes que requieren largas descargas y ocupan mucho espacio en disco . Es posible que una aplicación se cree en una plataforma específica (por ejemplo , Java ) y requiera que se instale esa plataforma, mientras que el 99% de sus otras aplicaciones no requieren soporte para esta plataforma. Este es un caso particular del problema cuando una aplicación usa una pequeña parte de una gran plataforma y eventualmente requiere la instalación de toda la plataforma (que solo puede resolverse refactorizando la aplicación), o una aplicación pequeña depende de una gran cantidad de diferentes bibliotecas al mismo tiempo.
La aplicación depende de la biblioteca "A" que depende de la biblioteca "B", ... que a su vez depende de la biblioteca "Z". Este es un caso especial del problema de muchas dependencias , con la diferencia de que un gran número de dependencias se complican por su intrincada y larga relación, que debe resolverse manualmente. (El usuario instala la aplicación, pero se le pide que instale la biblioteca "A", instala la biblioteca "A", pero cuando intenta hacerlo, la biblioteca "A" solicita la instalación de la biblioteca "B", etc.
A veces, tener una cadena de dependencia tan larga puede generar conflictos en los que diferentes componentes de la cadena requieren diferentes versiones del mismo paquete o biblioteca. (ver dependencias en conflicto ) Estas largas cadenas de dependencias deben ser manejadas por administradores de paquetes , que lo hacen automáticamente, en lugar de obligar al usuario a resolverlas manualmente, lo que puede dejar dependencias parcialmente insatisfechas (no todos los instaladores de bibliotecas le recuerdan constantemente todos sus usuario de dependencias).
Si la "Aplicación 1" depende de la biblioteca "A" versión 1.2 y la "Aplicación 2" depende de la misma biblioteca "A", pero ya la versión 1.3 , y no se pueden instalar diferentes versiones de la biblioteca "A" al mismo tiempo, entonces La "Aplicación 1" y la "Aplicación 2" no se pueden usar al mismo tiempo (ni siquiera se pueden instalar si los instaladores verifican las dependencias).
Cuando es posible utilizar diferentes versiones de la biblioteca al mismo tiempo, esto se soluciona mediante instalaciones paralelas de diferentes versiones de la biblioteca. De lo contrario, la instalación con la nueva versión de la biblioteca debe ir acompañada de la eliminación de la versión anterior de la biblioteca y, en consecuencia, todos los programas que dependen de ella. serán inoperables.
En los sistemas Linux , este problema suele ocurrir al intentar instalar paquetes de diferentes desarrolladores en el sistema que no están destinados a esta versión del sistema. En tal caso, satisfacer una larga cadena de dependencias de paquetes puede llevar, por ejemplo, a solicitar una versión diferente de la biblioteca glibc , una de las bibliotecas fundamentales más importantes del sistema . Si esto sucede, se le pedirá al usuario que elimine miles de paquetes, lo que es esencialmente lo mismo que eliminar, por ejemplo, el 80 % del sistema, incluidos los shells gráficos y cientos de programas diferentes.
Una situación en la que la aplicación "A" versión 2 depende de la aplicación "B", que depende de la aplicación "C", que a su vez depende de la aplicación "A", pero la versión 1. Esto lleva al hecho de que en sistemas por lotes como RPM o DPKG , el usuario debe instalar dos versiones de la misma aplicación de origen "A", lo que puede no estar permitido o permitido por el administrador de paquetes. Sin embargo, en los sistemas Linux , la presencia de una dependencia circular suele ser el resultado de un malentendido del usuario sobre cómo usar el sistema operativo y su administrador de paquetes.
La solución más obvia (y comúnmente utilizada) al problema es la numeración de versiones estandarizada, en la que el software usa un número específico para cada versión (también conocida como versión principal ) y un número adicional para la versión secundaria (también conocida como versión secundaria ), como: 10.1 , o 5.7 . Una versión principal cambia solo cuando el programa que tiene esa versión ya no es compatible con el programa actualizado, teniendo en cuenta los cambios realizados en él. Una versión menor puede cambiar incluso con pequeños cambios en el código que no impiden que el software de terceros funcione con el programa desarrollado. En casos como este, los programas de terceros pueden simplemente solicitar un componente que tenga una versión principal específica y una versión secundaria arbitraria (superior o igual a la versión secundaria especificada). Todo seguirá funcionando y las dependencias se resolverán con éxito incluso si la versión secundaria ha cambiado.
La solución de numeración de versiones se puede mejorar haciendo que la numeración de versiones sea compatible en el nivel del sistema operativo . Esto permitirá que la aplicación consulte el módulo/biblioteca por su nombre único y establezca restricciones en los números de versión, utilizando efectivamente el sistema operativo. Un módulo compartido se puede colocar en un almacén central sin el riesgo de falla de las aplicaciones que dependen de versiones anteriores o posteriores de ese módulo. Cada versión tiene su propia entrada en el repositorio, estando al lado de otras versiones del mismo módulo. Esta solución se ha utilizado en el sistema operativo Windows desde Windows Vista , donde Global Assembly Cache es una implementación de dicho repositorio central con servicios asociados y un administrador de paquetes integrado.
Los administradores de paquetes administrados mediante programación pueden actualizar componentes de software independientes y, al mismo tiempo, resolver incompatibilidades de versiones principales.
Muchas distribuciones modernas de Linux tienen administradores de paquetes basados en repositorios para lidiar con el problema de las dependencias. Estos sistemas son una capa sobre RPM , dpkg u otros sistemas de paquetes, y están diseñados para resolver automáticamente las dependencias mediante la búsqueda en un repositorio de software predefinido . Por lo general, estos repositorios de software son FTP o sitios web, directorios en una computadora local o distribuidos a través de redes informáticas o, con menos frecuencia, directorios en medios extraíbles como CD o DVD. Esto elimina el infierno de dependencia para los paquetes de software almacenados en repositorios que normalmente mantienen los proveedores de distribución de Linux y los espejos de todo el mundo. Además, estos repositorios a menudo son grandes, es imposible tener todas las piezas de software en ellos a la vez, por lo que aún puede establecerse un infierno de dependencia. En cualquier caso, los mantenedores de repositorios se enfrentan al infierno de la dependencia de una forma u otra. [3] Ejemplos de tales sistemas son APT , YUM , urpmi , Zypper , Portage , Pacman y otros.
PC-BSD (sistema operativo basado en FreeBSD ) hasta la versión 8.2 maneja el infierno de dependencias colocando paquetes y dependencias en directorios de contenedores independientes, evitando así daños a las bibliotecas del sistema durante las actualizaciones u otros cambios en ellas. El sistema PC-BSD usa "PBI" (Push Button Installer) como su principal administrador de paquetes. [cuatro]
Debido a que diferentes piezas de software tienen diferentes dependencias, es posible entrar en un círculo vicioso de requisitos de dependencia, o (quizás peor) en un árbol de requisitos en constante expansión , ya que cada nuevo paquete requiere algunos más para instalarse. Los sistemas como APT pueden permitir esto dando al usuario una variedad de opciones para elegir y permitiéndole aceptarlas o rechazarlas como desee.
Si el software de la aplicación está diseñado de tal manera que sus desarrolladores puedan adaptar fácilmente la interfaz que trata con el sistema operativo , el administrador de ventanas o el entorno de escritorio a estándares nuevos o cambiantes, entonces los programadores solo tendrían que controlar las notificaciones de los creadores del entorno o Los diseñadores de bibliotecas adaptan rápidamente sus actualizaciones de software a las actualizaciones de los usuarios, los costos serían mínimos y consumirían mucho tiempo, por lo que no serían necesarias actualizaciones costosas. Este método alentaría a los programadores a involucrarse activamente con aquellos de los que dependen para mantener un proceso de notificación razonable que no suponga una carga para nadie involucrado.
Otro enfoque para prevenir problemas de dependencia es implementar software a través del dispositivo . Las dependencias se encapsulan en un módulo, lo que permite a los usuarios no preocuparse por las dependencias del software. Esta es la preocupación de los desarrolladores de software.
En este caso, nos referimos a aplicaciones (o versiones de aplicaciones estándar) que funcionan en un entorno propio, cerrado y autosuficiente y que tienen una dependencia mínima de las bibliotecas del sistema. Todos los componentes necesarios para el funcionamiento del programa se agregan en la etapa de desarrollo interno, codificación y empaquetado, mientras que los archivos y componentes importantes para el funcionamiento del programa se encapsulan tanto como sea posible en un entorno independiente del resto de el sistema, como resultado, mediante un impacto mínimo con el resto del sistema, es posible evitar la mayoría de los problemas con dependencias no resueltas. A menudo puede funcionar independientemente del sistema en el que se ejecute la aplicación. Las aplicaciones en RISC OS y ROX Desktop en un entorno Linux usan directorios de aplicaciones , por lo que funcionan con un principio similar: un programa con todas sus dependencias está contenido en su propio directorio (carpeta) autónomo. [5]
En ciertas plataformas de software, el concepto de "infierno de dependencia" recibe su propio nombre, según el nombre de los componentes en conflicto.