En el desarrollo de software, el Configuration Management ocupa un lugar destacado dentro del proceso de delivery, ya que se debe asegurar la integridad de lo instalado y la correspondencia con el o los repositorios del producto y sus componentes.
Asegurar la integridad de lo instalado implica que haya coherencia entre los distintos componentes que forman la Solución o el Sistema mencionado. Esto implica que las versiones que se instalan sean compatible entre sí en dos niveles:
1.- Que se comuniquen entre ellas sin errores.
2.- Que trabajando juntas cumplan con el objetivo del Sistema.
Para aclarar la diferencia entre los dos conceptos, podríamos pensar dos componentes que tengan que comunicarse pero tengan distintas versiones de Protocolo incompatibles entre sí o una aplicación que deba obtener datos de una DB cuyas tablas están creadas para otra versión, sería un ejemplo del primer caso.
Por otro lado, si los Componentes se comunican, pero la interpretación de los datos es distinta entre ellos, o el procesamiento de los mismos es distinto, sería un ejemplo del segundo caso.
LLevar un sistema de Configuration Management efectivo no es tarea fácil, sobre todo cuando existen varias líneas bases activas para una determinada Solución o Sistema.
Que sea efectivo implica que por un lado sea preciso y por otro lado lo suficientemente flexible para atender los distintos escenarios del Delivery.
Varias líneas base activas para una misma Solución quiere decir que hay instalaciones en clientes con distintas versiones no compatibles entre si en su totalidad, ya que son de diferentes “sabores”.
Si bien a priori tener múltiples versiones activas en clientes no es una práctica recomendada, es habitual que ésto suceda cuando una Solución está difundida en varios clientes distintos.
Una forma de encarar este problema desde el diseño es tener una Arquitectura modular, en donde los componentes que proveen funcionalidad se instalan o no dependiendo de que esa funcionalidad sea necesaria para la instalación en cuestión y que a la vez la funcionalidad particular sea “apagable”.
En éste tipo de Arquitectura cada Componente tiene su propio repositorio, roadmap y versionado.
En la Figura 1 se muestra una Solución compuesta de varios Componentes de distintas versiones. En éste caso, la Línea base (Ej: 1.2.3) está compuesta de los componentes resaltados y las versiones que corresponden de cada uno.
Podría darse que una determinada versión de la Solución no utilice alguno de los componentes también.
Hasta acá nos encontramos con una complejidad “media”, en donde la Configuración de la Línea Base se realiza tomando la versión que corresponde (versión integrada) de los Componentes deseados. Pero…
(1) ¿Qué pasa cuando tenemos distintas versiones de alguno de los componentes que están activas?
(2) ¿Y si tenemos funcionalidad compartida entre varios?
(3) ¿Y si la Solución tiene una baja modularidad? (Monolítica)
Vayamos por partes…
Ahora Imaginemos el siguiente escenario: El Componente A, que actualmente va por la versión 1.2.0, está integrado en éste sistema con la versión 1.1.15, como indica la Figura 1.
El Cliente A pide un cambio o nueva funcionalidad sobre la versión que tiene instalada y por temas de negocio (esfuerzo y tiempo de desarrollo, complejidad, etc) se decide modificar la versión 1.1.15 del Componente A y no la 1.2.0 (última versión). Esto implica abrir un nuevo branch del componente en donde ahora quedarían “activas” dos ramas (es decir con posibilidad de hacer cambios”)
En este momento la organización puede verse tentada a dejar este nuevo “sabor” del componente en ese branch y no agregar la funcionalidad al branch principal.
Ésto puede ser un gran problema a futuro, como podemos ver cada día.
El equipo y su líder, junto con Producto, tienen que definir previamente al desarrollo si ésta nueva funcionalidad será parte del producto en el futuro o no. Si no se desea que ésta funcionalidad sea parte del producto en el futuro, debería desarrollarse en un componente a parte. Si se decide que ésta funcionalidad sea parte del producto, entonces debe agregarse la misma en la rama principal, generando la versión 1.2.1.
Debería anotarse como Deuda Técnica la integración de la versión 1.2.1 en la línea base del Sistema, para evitar que queden las dos ramas activas.
No olvidemos que dos ramas activas implican más esfuerzo en mantenimiento.
Branching y Releases
Durante el mantenimiento y roadmap de un Producto o Componente tenemos tres macro tareas fundamentales: fixes, mejoras y nuevas funcionalidades. Como la línea entre fix y mejora a veces puede ser difusa, podríamos resumirlo en dos macro tareas: fixes y nuevas funcionalidades. Cualquiera de éstas dos macro tareas pueden ser disparadas internamente, por el trabajo diario de Mejora de Productos o Roadmap, o desde uno o más clientes que tienen instalado el producto.
Para poder lograr que todo ésto marche de manera organizada se deben generar pŕocedimientos de branching y release que lo permitan. Entre los varios disponibles nosotros elegimos éstas reglas básicas:
- Por cada versión instalada en un cliente tenemos un branch de Development de esa versión. En ese branch se trabaja en el desarrollo continuo de mejoras y fixes.
- Se deben mergear los cambios realizados en “Dev” al branch de Master de esa versión previamente a un Release.
- Si se quiere trabajar un New Feature, se debe abrir un branch (Feature Branch) para trabajarlo, que luego será mergeado a al branch de Master.
- No se realizan releases desde un Feature Branch o de Dev
Con éstas reglas, el esquema de branching queda de la siguiente manera: