Gestión de Ramas en Control de Versiones: Git Flow vs. Trunk Based Development

Para desarrollar software de calidad, necesitamos poder obtener todos los cambios en nuestro proyecto de forma rápida y segura y poder revertirlos si es necesario con facilidad. Estoy hablando específicamente de las políticas de gestión de ramas en Git, para poder fusionar los cambios realizados por los distintos squads de desarrollo; estas buenas prácticas aceleran enormemente el trabajo y nos permiten encontrar errores con mayor facilidad.

Además, trabajar en squads en modo Agile con 7 evolutivos y más de 50 desarrolladores es principalmente posible gracias a estas técnicas o herramientas, según se interprete. Como objetivo principal, los desarrolladores trabajan en diferentes partes de una aplicación al mismo tiempo y luego unen sus resultados en un solo producto. Vamos a echarle un ojo al control de versiones y entenderemos cómo surgieron estas técnicas.

Cómo los Sistemas de Control de Versiones Cambiaron el Mundo del Desarrollo de Software

Antes de que se crearan los sistemas de control de versiones, la gente confiaba en hacer copias de seguridad manuales durante el ciclo de vida de los proyectos. Se copiaban archivos modificados a mano para ir incorporando poco a poco el trabajo de varios desarrolladores en el mismo proyecto. El mayor impacto de esta técnica es que cuesta mucho tiempo, espacio en discos duros y, claro está, dinero para las empresas.

Podemos distinguir 3 generaciones de control de versiones:

  • Locks -> RCS
  • Merge before commit -> Subversion, CVS
  • Commit before Merge -> Git, Mercurial

Notamos que, a medida que maduran los sistemas de control de versiones, existe una tendencia a aumentar la capacidad de trabajar en proyectos en paralelo. Uno de los cambios más innovadores fue pasar de bloquear archivos a fusionar cambios, lo que permitió a los programadores trabajar de manera más eficiente.

Otra mejora considerable fue la introducción de sistemas distribuidos. Git fue una de las primeras herramientas en incorporar esta filosofía. Literalmente permitió que floreciera el mundo del código abierto. Git permite a los desarrolladores copiar todo el repositorio, en una operación llamada forking (bifurcación), e introducir los cambios deseados sin necesidad de preocuparse por los conflictos de fusión.

Diagrama de la evolución de los sistemas de control de versiones

Más tarde, pueden iniciar una solicitud de extracción (Pull Request) para fusionar sus cambios en el proyecto original. Si el desarrollador inicial no está interesado en incorporar esos cambios de otros repositorios, entonces puede convertirlos en proyectos separados por su cuenta. Todo es posible gracias a que no existe un concepto de almacenamiento central.

Hoy en día, el sistema de control de versiones más popular es, sin duda, Git, con una participación de mercado de alrededor del 70 por ciento en 2016. Git se popularizó con el auge de Linux y la escena del código abierto en general. GitHub, actualmente el almacenamiento en línea más popular para proyectos públicos, también contribuyó considerablemente a su prevalencia. Le debemos la introducción de solicitudes de extracción fáciles de administrar a Git.

En pocas palabras, las solicitudes de extracción son solicitudes creadas por un desarrollador de software para combinar los cambios que crearon con el proyecto principal. Incluye un proceso de revisión de esos cambios. Los revisores pueden insertar comentarios sobre cada parte que crean que podría mejorarse o que consideren innecesaria. Después de recibir comentarios, el creador puede responderlos, crear una discusión o simplemente seguirlos y cambiar su código en consecuencia.

Ilustración de un Pull Request en GitHub

Git es simplemente una herramienta. Puedes utilizarlo de muchas formas diferentes. Actualmente, los dos estilos de desarrollo más populares que puedes encontrar son Git Flow y Trunk Based Development. Muchas veces las personas se familiarizan con uno más que el otro y se puede descuidar su gran uso.

Git Flow

En el modelo de desarrollo de Git Flow, existe una rama de desarrollo principal con acceso restringido a ella (una buena práctica, por cierto), normalmente se le llama la rama develop. El squad crea ramas con el nombre feature desde la rama principal, es decir, develop, y trabajan sobre ellas. Una vez que se termina el desarrollo y el squad de QA valida las pruebas, se crean los famosos Merge Request o Pull Request. En dicha fase, los revisores deben estar muy pendientes del código que está solicitando su fusión con develop; ahí pueden tener discusiones, y a menudo se crean ciclos de resolución de MR o PR para aceptar o no dicha parte del código dentro del proyecto.

Una vez que la MR o PR es aceptada, se realiza la inclusión de esa parte del proyecto en la rama develop. En este punto, los revisores asumen un papel muy importante. En algún proyecto escuché una referencia que me marcó y me pareció muy elegante: el rol de "Torre de Control". Eso nos hace pensar en un aeropuerto, pues claro, las features son vuelos y la torre de control debe revisar las condiciones para que este vuelo aterrice o no y evitar los conflictos. ¡Genial, ¿no?! Por otro lado, una muy buena práctica es la comunicación inmediata dentro del squad de desarrollo de que la rama develop ha sufrido cambios y que los distintos desarrolladores deben actualizar sus features lo más pronto posible para evitar futuros conflictos.

Diagrama del flujo de trabajo Git Flow

Ventajas y Desventajas de Git Flow

Una de las ventajas de Git Flow es el control estricto. Solo los desarrolladores autorizados pueden aprobar los cambios después de observarlos de cerca, lo que garantiza la calidad del código y ayuda a eliminar errores de forma temprana. Sin embargo, debes recordar que también puede ser una gran desventaja, ya que crea un embudo que ralentiza el desarrollo de software. Si la velocidad es tu principal preocupación, entonces podría ser un problema grave. Las características desarrolladas por separado pueden crear ramas duraderas que pueden ser difíciles de combinar con el proyecto principal.

Además, los MR o PR pueden conducir a una amplia microgestión, en la que el desarrollador principal gestiona literalmente cada línea de código. Si tienes desarrolladores experimentados en los que puedes confiar, ellos pueden manejarlo, pero es posible que estés perdiendo tu tiempo y sus habilidades. También puedes desmotivar gravemente a los desarrolladores.

¿Cuándo funciona mejor Git Flow?

  • Cuando se ejecuta un proyecto de código abierto: Este estilo tiene su origen en el mundo open source y funciona mejor allí, dado que todos pueden contribuir y es necesario tener acceso a todos los cambios. Con el objetivo de verificar las líneas de código, impacta directamente en la fiabilidad de los desarrolladores. Normalmente, este tipo de enfoques no son para proyectos comerciales y el tiempo de entrega no es una preocupación.
  • Cuando hay muchos programadores junior: Si dentro de tu proyecto hay perfiles junior de desarrollo, lo más importante es verificar su trabajo de cerca. Puedes darles sugerencias sobre cómo trabajar más eficientemente y ayudarles a mejorar sus habilidades más rápido. El foco de la Torre de Control está en ese punto; ahí se podría dar el caso de comprometer la rama develop.
  • Cuando tienes un producto ya establecido: Si tienes un proyecto consolidado, la atención se centra en la mejora del rendimiento de la aplicación. Este tipo de optimizaciones requiere cambios casi quirúrgicos y muy precisos. Ahí entran los Seniors al frente de la Torre de Control. Además, las empresas se adaptan perfectamente a este estilo.

¿Cuándo puede Git Flow ser un problema?

  • Cuando recién estás comenzando: En este caso, un Arquitecto de Software está construyendo la base del proyecto y no requiere más manos para ello. Si en el caso de que se esté trabajando en una parte de la app sin tener la arquitectura resuelta, lo más probable es que se presente un cuello de botella en ese punto.
  • Cuando necesitas iterar rápidamente: Con Agile, es una de sus características más importantes. Una vez que el MVP se ha liberado, lo más probable es que debas cambiarlo y modificarlo varias veces para estabilizar la app con el fin de satisfacer las necesidades de nuestro cliente. Nuevamente, múltiples features o MR o PR generarán cuellos de botella y reducirán drásticamente la velocidad de desarrollo.
  • Cuando trabajas principalmente con desarrolladores senior: En el caso de que cuentes principalmente con desarrolladores senior, que han trabajado entre ellos durante años, no se necesita una microgestión de MR o PR. La principal razón es que confías en tus desarrolladores y sabes que son profesionales.

Trunk Based Development

En el modelo de desarrollo basado en Trunk Based Development, todos los desarrolladores trabajan en una sola rama con acceso abierto a ella. Normalmente es la rama master. Envían su código y lo ejecutan, ¡súper simple! ¿Pero es seguro?

En algunos casos, se crean ramas feature de corta duración. Una vez que el código en su rama compila y pasa todas las pruebas, se fusiona directamente con master. Esta metodología garantiza que el desarrollo sea verdaderamente continuo y evita que los desarrolladores creen conflictos que son difíciles de resolver.

Diagrama del flujo de trabajo Trunk Based Development

La única forma de revisar el código en este enfoque es hacer una revisión completa del código fuente. Por lo general, las discusiones prolongadas son limitadas. Nadie tiene un control estricto sobre lo que se está modificando en la base del código fuente; por eso es importante tener un estilo de código ejecutable. Los desarrolladores que trabajan con ese estilo deben tener experiencia para que se sepa que no reducirán la calidad del código fuente.

Este estilo de trabajo puede ser maravilloso cuando trabajas con un equipo de desarrolladores de software senior. Les permite introducir nuevas mejoras rápidamente y sin burocracia innecesaria. También les muestra que confías en ellos, ya que pueden introducir código directamente en la rama master. Los desarrolladores en este flujo de trabajo son muy autónomos: entregan directamente y se verifican los resultados finales en el producto de trabajo.

Conceptos Clave en Control de Versiones

Se llama control de versiones a la gestión de los diversos cambios que se realizan sobre los elementos de algún producto o una configuración del mismo. Aunque un sistema de control de versiones puede realizarse de forma manual, es muy aconsejable disponer de herramientas que faciliten esta gestión, dando lugar a los llamados sistemas de control de versiones o VCS (del inglés Version Control System).

Estos sistemas facilitan la administración de las distintas versiones de cada producto desarrollado, así como las posibles especializaciones realizadas (por ejemplo, para algún cliente específico). El control de versiones se realiza principalmente en la industria informática para controlar las distintas versiones del código fuente, dando lugar a los sistemas de control de código fuente o SCM (siglas del inglés Source Code Management).

Elementos Fundamentales

  • Repositorio: El repositorio es el lugar en el que se almacenan los datos actualizados e históricos de cambios, a menudo en un servidor. A veces se le denomina depósito o depot.
  • Revisión: Una revisión es una versión determinada de la información que se gestiona. Hay sistemas que identifican las revisiones con un contador (Ej. Subversion). Hay otros sistemas que identifican las revisiones mediante un código de detección de modificaciones (Ej. Git usa SHA1). A la última versión se le suele identificar de forma especial con el nombre de HEAD.
  • Etiqueta (Tag): Darle a alguna versión de cada uno de los ficheros del módulo en desarrollo en un momento preciso un nombre común ("etiqueta" o "rótulo") para asegurarse de reencontrar ese estado de desarrollo posteriormente bajo ese nombre. En la práctica se rotulan todos los archivos en un momento determinado. Para eso el módulo se "congela" durante el rotulado para imponer una versión coherente. Los tags permiten identificar de forma fácil revisiones importantes en el proyecto.
  • Rama (Branch): Un módulo puede ser branched o bifurcado en un instante de tiempo de forma que, desde ese momento en adelante se tienen dos copias (ramas) que evolucionan de forma independiente siguiendo su propia línea de desarrollo. El módulo tiene entonces 2 (o más) "ramas". La ventaja es que se puede hacer un "merge" de las modificaciones de ambas ramas, posibilitando la creación de "ramas de prueba" que contengan código para evaluación. Si se decide que las modificaciones realizadas en la "rama de prueba" sean preservadas, se hace un "merge" con la rama principal.
  • Despliegue (Checkout): Crea una copia de trabajo local desde el repositorio.
  • Conflicto: Un conflicto ocurre cuando el sistema no puede manejar adecuadamente cambios realizados por dos o más usuarios en un mismo archivo. El sistema es incapaz de fusionar los cambios.
  • Cambio (Change/Diff): Un cambio representa una modificación específica a un archivo bajo control de versiones. En muchos sistemas de control de versiones con commits multi-cambio atómicos, una lista de cambios identifica el conjunto de cambios hechos en un único commit.
  • Copia de Trabajo (Working Copy): La copia de trabajo es la copia local de los ficheros de un repositorio, en un momento del tiempo o revisión específicos. Todo el trabajo realizado sobre los ficheros en un repositorio se realiza inicialmente sobre una copia de trabajo, de ahí su nombre.
  • Fusión (Merge): Una fusión o integración es una operación en la que se aplican dos tipos de cambios en un archivo o conjunto de archivos.

Tipos de Sistemas de Control de Versiones

  • Centralizados: Existe un repositorio centralizado de todo el código, del cual es responsable un único usuario (o conjunto de ellos). Se facilitan las tareas administrativas a cambio de reducir flexibilidad, pues todas las decisiones fuertes (como crear una nueva rama) necesitan la aprobación del responsable. Necesita menos veces estar conectado a la red para hacer operaciones.
  • Distribuidos: Cada usuario tiene su propio repositorio. Los distintos repositorios pueden intercambiar y mezclar revisiones entre ellos. Es frecuente el uso de un repositorio, que está normalmente disponible, que sirve de punto de sincronización de los distintos repositorios locales. Al hacer de los distintos repositorios una réplica local de la información de los repositorios remotos a los que se conectan, la información está muy replicada y, por tanto, el sistema tiene menos problemas en recuperarse si, por ejemplo, se quema la máquina que tiene el repositorio remoto. Por tanto, hay menos necesidad de backups.

Esquema comparativo de sistemas de control de versiones centralizados y distribuidos

Flujos de Trabajo Comunes

  • Flujo de Trabajo Centralizado (Centralized Workflow): Cada desarrollador es un nodo de trabajo. Por otro lado, hay un repositorio remoto central que funciona a modo de punto de sincronización. Una desventaja de este modo de trabajo es que si dos usuarios clonan desde un punto central y ambos hacen cambios; tan solo el primero que envíe sus cambios lo podrá hacer limpiamente. El segundo desarrollador deberá fusionar previamente su trabajo con el del primero, antes de enviarlo, para evitar sobreescribir los cambios del primero.
  • Flujo de Trabajo con un Gestor de Integraciones (Integration-Manager Workflow): Cada desarrollador tiene acceso de escritura a un repositorio propio público y acceso de lectura a los repositorios públicos de todos los demás usuarios. Para contribuir en estos proyectos, cada desarrollador crea su propio clon público del repositorio canónico y envía sus cambios (realizados en un repositorio privado) a él. La principal ventaja de esta forma de trabajar es que puedes continuar trabajando, y la persona gestora del repositorio canónico podrá recuperar tus cambios en cualquier momento.
  • Flujo de Trabajo con Dictador y Tenientes (Dictator and Lieutenants Workflow): Es una ampliación del flujo de trabajo con gestor de integraciones. Hay una serie de gestores de integración que se encargan de partes concretas del repositorio, a los que se denominan tenientes. Todos los tenientes rinden cuentas a un gestor de integración, conocido como el dictador.

Metodologias Git flow y trunk based

El Poder de las Ramas

Las ramas, en un sistema de control de versiones, constituyen una potente herramienta que flexibiliza la forma en que los colaboradores cooperan en el proyecto (en inglés Branching Workflows). Las ramas son solo una herramienta que es posible utilizar de distintas formas para conseguir distintos objetivos.

En algunos proyectos se tienen varias ramas siempre abiertas, que indican diversos grados de estabilidad del contenido. Por ejemplo, en la rama master es frecuente mantener únicamente lo que es totalmente estable. Luego se tienen otras ramas que revelan distintos grados de estabilidad. Por ejemplo, podríamos tener una rama beta (versión beta) y otra alfa (versión alfa), en las que se va trabajando.

Las ramas puntuales, también llamadas ramas de soporte, son ramas que se crean de forma puntual para realizar una funcionalidad muy concreta. Este tipo de ramas permiten trabajar centrándonos exclusivamente en el desarrollo de una característica concreta y, cuando esta esté concluida, se fusiona con una de las ramas de largo recorrido (normalmente con la de más bajo nivel de estabilidad, para que sea probada en ese entorno). La fusión solo se realiza cuando se está 'seguro' de que esa característica está correctamente implementada, en lugar de fusionar en el orden en que se van desarrollando las cosas. Esto permite, por un lado, tener un historial de las distintas versiones que se han tenido hasta conseguir la funcionalidad. El uso de este tipo de ramas permite más flexibilidad a la hora de probar posibles soluciones.

Un tipo de ramas de este tipo que tienen un funcionamiento especial son las llamadas ramas de versión o ramas de release (en inglés release branch). Este tipo de ramas se crean para dar soporte a la preparación de una nueva versión de producción. Es frecuente y una buena práctica utilizar en el nombre de la rama un prefijo que indique el tipo de rama de la que se trata.

tags: #el #tronco #o #trunk #o #main