Search

Search this site:

Arquitecturas de paquetes al rescate

Modularización: Arquitecturas de paquetes al rescate

Convirtiendo a la hidra en nuestra aliada

En el número 27 de Software Gurú, Agustín Ramos presentó un artículo acerca de estrategias para lograr una modularización efectiva en Java. Varios de los puntos de su artículo me llevaron a dedicar la presente columna a explicar a qué llamamos una distribución de Linux, y cuál es su relación –y la solución que ofrece– a los problemas derivados de la complejidad derivada de la modularización, que bien describió Agustín.

Como deben ya saberlo, a diferencia de otros sistemas operativos, el entorno operativo completo al que normalmente nos referimos como Linux no es desarrollado por un sólo grupo, ni sigue un roadmap, criterios o visión en común. Mucho se ha argumentado acerca de las características que diferencían a los proyectos desarrollados desde planteamientos libres de aquellos desarrollados siguiendo metodologías tradicionales — No abordaré dicha discusión en este momento. Sin embargo, no quiero dejar de aprovechar la oportunidad de tener un artículo escrito por un colega en esta revista para ilustrar cómo abordamos la relación entre proyectos independientes –que logran una complejidad de miras mucho mayor que cualquier instancia de conjuntos de módulos desarrollados en casa– en el Software Libre, por qué enfatizamos tanto en lo prevalente que resulta dicha modularización, y cómo nos enfrentamos a su inherente complejidad.

Los sistemas basados en Linux son estructurados, pues, en distribuciones. Una distribución es un conjunto de programas –típicamente del órden de miles– que, entre todos, presentan la funcionalidad que un usuario espera ya no sólo de un sistema operativo sino que de todo un entorno operativo integrado. Además, se encargan de gestionar las relaciones de dependencia entre dichos programas, y resolver dichas dependencias, para facilitar al usuario la instalación, remoción y actualización de software.

Una instalación mínima de cualquier distribución de Linux no tiene menos de un par de centenares de paquetes individuales. El núcleo Linux, por sí mismo, no representa más que la interfaz más básica para presentar una abstracción del hardware ante los programas que corren sobre él. Formalmente, al mencionar Linux ni siquiera nos referimos a la interfaz de línea de comandos (misma que generalmente es provista por los paquetes bash, dash o sash, dependiendo de las necesidades del administrador). Son en realidad contados los paquetes que no dependen más que de paquetes esenciales. Toda distribución define un pequeño conjunto (decenas) de programas que es fundamental tener instalados como base para cualquier otro, conjunto mínimo que asumimos que siempre estará ahí para asegurarnos la funcionalidad mínima.

Si bien las primeras distribuciones tuvieron por objetivo facilitar la instalación de un sistema basado en Linux a usuarios con menor involucramiento técnico, y fueron instrumentales en la primer ola expansiva de usuarios que fuimos adoptando Linux hacia mediados de los 1990, han trascendido ya a éste rol para dar respuesta al infierno de dependencias al que se refiere en su texto Agustín Ramos: Hacia fines de los 1990, aproximadamente cuando todas las distribuciones comenzaban a contar en miles los paquetes independientes que ofrecían, comenzó a hacerse obvio que no bastaba con que cada paquete indicara de qué otros paquetes dependía (incluyendo, claro está, la información de versiones pertinente), sino que era necesario contar con una arquitectura de paquetes: Un esquema orientado a depósitos y no a paquetes individuales, que se encargara de resolver las dependencias cada que el administrador instalara o eliminara un paquete. La primera arquitectura fue introducida por la distribución Debian, bajo el nombre de apt: A Package Tool. Es definitivamente gracias a apt que, al día de hoy, la versión de desarrollo de Debian cuenta con 15640 paquetes fuente independientes, que resultan en 27886 paquetes binarios, cubriendo prácticamente todas las áreas de necesidad en cómputo, tanto a nivel de aplicaciones como de bibliotecas.

A diferencia de otras arquitecturas previas, como los ports de los Unixes BSD, Apt está además construido basado en el manejo de depósitos múltiples. Esto significa que, además de servirme para instalar los paquetes oficiales de la distribución, nos permite definir depósitos adicionales con el software que desarrollemos localmente, así como de paquetes adicionales que preparemos localmente para uso en nuestra organización.

Con esto como introducción, veamos cómo ésto se aplica al texto de Agustín. Por razones de espacio, me enfoco a cómo éste esquema reduce fuertemente los efectos negativos de la modularización, permitiendo a los desarrolladores crear software más robusto y temer menos a esta tan dificil de conciliar fuente de dolores de cabeza.

Demasiadas dependencias
El argumento principal mencionado en el texto al que hago referencia en contra de tener demasiadas dependencias es la posterior dificultad de instalación de nuestros sistemas. Sin embargo, al crear paquetes con la información de las dependencias, convertimos el fastidioso proceso de instalación (y todo el tiempo que requiere documentarlo) en una sóla instrucción al sistema.
Dependencias cíclicas
Coordinar el trabajo de cientos de voluntarios en todo el mundo, sin más factores de cohesión que su voluntad por crear un sistema de calidad trae como resultado natural el que parte importante de sus esfuerzos estén encaminados a la creación de documentos de políticas comprehensivos — y a que su comunidad de desarrolladores comprenda la importancia de dichos documentos.
Claro está, si la unidad atómica de trabajo en una distribución es el paquete, problemas tan claros como las dependencias cíclicas fueron de los primeros puntos en ser prohibidos por política. Sin embargo, conforme la complejidad de cada uno de los paquetes aumenta, se vuelve posible que aparezcan dependencias cíclicas de indirectas. Para evitar problemas como este, y otros destinados del control de calidad en sistemas complejos, se hacen revisiones periódicas de todos los procesos imaginables.
En este aspecto, si bien no hay balas de plata para evitar las dependencias cíclicas, contamos con una comunidad de desarrolladores verificando que estos problemas se mantengan al mínimo. No estamos sólos en el manejo de nuestros proyectos.
Largas cadenas de dependencias
Este fue precisamente el punto que motivó al desarrollo de las arquitecturas de paquetes. Podemos confiar en que la arquitectura que elijamos, empleando los depósitos del sistema y de nuestra organización, sepan resolver este punto sin que represente problemática alguna. Además, no tenemos por qué limitarnos al uso de este esquema para las dependencias de los componentes externos que empleemos. Si dentro de nuestra organización nos acostumbramos a empaquetar nuestro código en componentes, la reutilización de código que podamos hacer será muchísimo más simple y más natural.
Dependencias en conflicto
Nuevamente, aquí acudimos a la sabiduría de las masas, a la fuerza de la multitud. Una distribución basada en Linux no es sólo un conjunto de programas disponibles a través de un mismo depósito — La mayor parte del trabajo de sus creadores es asegurarse que todos los componentes sean mutuamente compatibles, y asegurar a los usuarios un producto de calidad, con todas las actualizaciones necesarias para asegurar su seguridad (cuidando no compormeter su estabilidad) durante su ciclo de vida.
Este puede ser un punto fundamental al elegir qué distribución vamos a usasr para determinado proyecto: Hay distribuciones principalmente orientadas al perfil de usuario de escritorio, para el cual es fundamental tener siempre soporte completo para el último hardware, aceleración gráfica compelta y demás bondades. Sin embargo, para basar nuestros desarrollos empresariales, típicamente preferiremos las distribuciones con ciclos de vida más largos, con un mayor soporte a largo plazo.

Como pueden ver, manejar una arquitectura de paquetes simplifica algunas de las tareas más complicadas (y más ingratas) del desarrollo de software, el manejo de toda la talacha creada por los componentes que, a fin de cuentas, incluimos para ahorrarnos trabajo.

Cuando veo los instaladores típicos, que crean enormes amasijos (típicamente en forma de enormes instaladores .msi o archivos .jar que incluyen copia de todas las dependencias para evitar estos problemas), no exagero: Me dan ganas de llorar. Porque además, al tener varias copias de una misma biblioteca en el sistema, tengo la certeza de que en caso de aparecer algún defecto en alguna de ellas, habrá componentes de mi sistema que reciban la corrección en cuestión — pero habrá otros que no. Acostumbrarse al manejo de dependencias externas nos reduce la tentación de acudir al tan temido ligado estático, reduce el peso de nuestras imágenes de instalación (y de nuestros sistemas ya instalados), y ayuda fuertemente a mantener un mayor control de calidad en nuestros procesos como un todo.

Mucha gente evita aprovechar la modularización como medida preventiva para no perder la razón resolviendo dependencias y bugs creados por compatibilidad incompleta entre versiones. Sin embargo, dejar de usar buen software, ya existente y probado por terceros, sólo por no contar con las herramientas de seguimiento correctas es un triste error en el que debemos evitar caer. La hidra de la modularización a la que se refiere Agustín puede ser un mounstro mortal, pero si aprendemos a hablar con ella puede ser nuestro mayor aliado. Porque si dos cabezas piensan mejor que una, ¿qué decir de decenas de cabezas?

Attachments

Fotografía de la primera página (publicada) (965 KB)

Fotografía de la segunda página (publicada) (1073 KB)

Texto del artículo enviado para su publicación (11 KB)