Git y Branching Strategies

Entramos en terreno peligroso. La noche carece de luna, por lo que la iluminación es escasa, casi inexistente. Detrás nuestro se encuentra el abismo, lleno de desorganización. Delante, las estrategias de branching en Git. El problema es que para lograr llegar a ellas, hay que cruzar un río interminable.

Bueno, quizá estoy siendo demasiado trágico. Pero si es cierto que este tema es uno de los más complejos que me crucé durante años. Y si bien se pueden formular posibles soluciones, ninguna es perfecta.

Si no conoces Git, te dejo un curso gratuito que armé, y un artículo que habla sobre eso. En cuanto a Branching Strategy, trata sobre cómo podemos organizarnos con los branchs (ramas) en un flujo de trabajo donde un equipo está trabajando simultáneamente en el mismo proyecto.

Para dejarlo mas claro, pongamos un ejemplo: Creamos un nuevo repositorio y tenemos el main branch master. Acordamos que en dicho branch, se publicará siempre la versión de producción.

Por lo tanto, creamos un branch que sale de master llamado develop. Allí trabajaremos con el proyecto en estado de desarrollo y no tocaremos master hasta que estemos seguros que hay una versión estable en develop; acto seguido procederemos a crear un merge entre develop y master.

Esto es lo básico, pero sólo funcionaría en un proyecto lineal donde cada desarrollador que toque develop no tenga conflictos con los demás. Y esto no es realista, dado que es muy normal tocar la misma clase o hacer un refactor para una determinada funcionalidad que luego choque con otras.

Ahí es donde necesitamos una estrategia de branching, y la primera que aparece es GitFlow.

Primer Estrategia: GitFlow

GitFlow es muy famoso, lo usan muchas empresas. Seguramente nos resuelva la vida.

Y así se toman las mejores malas decisiones. Pero no me quiero adelantar, vamos a meternos en el flujo de trabajo con GitFlow:

  • Master sigue siendo la rama principal, el main branch. Es el reflejo de producción.
  • Develop mantiene su naturaleza de branch de desarrollo.
  • Cuando se arma una funcionalidad nueva, se abre un Feature Branch que sale desde Develop.
  • Cuando se cierra esa funcionalidad, se genera un merge a Develop y se elimina el branch de feature.
  • Si la funcionalidad no se aprobara, simplemente se elimina el feature sin tocar Develop.
  • Cuando surge un bug en producción, se crear un branch de Bugfix desde Master.
  • Cuando se aprueba todo lo que fue desarrollado en Develop, se genera una versión. Pero no se mergea directo a Master, sino que se genera un branch de Release que permanece abierto en una etapa de testing.
  • Una vez se aprueba el Release, se mergea a Master y Develop, se genera un tag de versión y se elimina.

Gráficamente pueden ver lo que acabo de explicarles:

El problema con GitFlow es la cantidad de mergeos que sufre nuestro repositorio. Además los Releases y Features pueden quedar obsoletos si se mantienen durante mucho tiempo en el desarrollo, principalmente porque Develop cambia con el curso del proyecto.

Cuantos más ramas tengamos en nuestro proyecto, más probabilidades hay de que se estanquen en el tiempo. Y cualquiera puede decir: Pero esto es cuestión de organización interna. Y yo les respondo: Intenta aplicar esa organización, y vas a notar lo complejo que es.

Para acompañar este modelo, les dejo un vídeo que describe a detalle cómo funciona esta estrategia:

Segunda Estrategia: Trunk-based Development

Hay muchas estrategias, pero yo pretendo enfrentar GitFlow y esta que voy a comentarles ahora.

Trunk se para sobre un concepto fundamental: eliminar la fricción de los branches. Demasiadas ramas provocan confusión, y si bien a primera vista parece ordenado, a largo plazo termina siendo un dolor de cabeza.

En este caso no existe el branch Develop. Y ahora estas confuso/a, porque todo el artículo se basa en la idea de que trabajemos nuestra instancia de desarrollo en esta rama. Ahora empieza lo bueno.

  • Los Features salen de Master.
  • Master sigue siendo nuestra main branch y el lugar donde se refleja producción.
  • Los Releases también salen de Master y se pueden mantener en el tiempo como pasaba con Gitflow.

Como ven, es muchísimo mas acotado comparada con la estrategia anterior.

Tercera Estrategia: La mía

Hace un par de párrafos mencioné que tomar la estrategia más famosa y considerar que nos va a funcionar por convención era un error.

La realidad es que la estrategia de branching es muy particular a cada empresa y proyecto que abordemos. Es como los patrones de diseño: No se pueden usar todo el tiempo con todo problema que queramos solucionar.

Hay que saber cuando acudir a un estándar, y cuando solucionar las cosas con criterio propio.

Cuando me incliné por abandonar Gitflow luego de haberlo usado un par de años, opté por Trunk. Pero incluso siendo una estrategia mas sencilla, no me terminó de convencer. Por ende empecé a trabajar en mi propia estrategia.

Arrancamos un proyecto de una app para alquilar departamentos. Creamos el branch Master en su versión 1.0.0 con la inicialización del proyecto. Por ejemplo, creando una sola Activity de Android:

Acto seguido, nos disponemos a trabajar en la versión 1.1.0, la cual traerá nuevas funcionalidades planificadas en nuestro Sprint, como la geolocalización para recomendar departamentos alquilables en la zona donde el usuario se encuentre. Para esto creamos un branch de release:

Mientras trabajamos en nuestra versión, surge una posible funcionalidad nueva basada en generar una notificación push cuando el usuario está a 100 metros del departamento. Puede que se integre a la versión final, pero no estamos seguros. Entonces generamos una versión de Feature que sale desde Release:

La versionamos como 1.1.1, siendo el tercer número el feature, y el segundo el release. El primer número debe considerarse como versión disruptiva y representa un gran cambio en nuestro producto.

Ahora vamos al caso que no suelen tocar la mayoría de las estrategias, lo cual me motivó a crear la mía: versiones paralelas.

Tiemblan las gradas de desarrolladores que están leyendo esto. Odiamos trabajar con proyectos paralelos, mucho peor si se trata del mismo. Para nosotros una versión tiene que continuar con otra. Pero en el día a día esto es una utopía.

Imaginemos que necesitamos una versión paralela. La geolocalización es importante, pero el mercado parece apuntar a que tener una lista de los departamentos más destacados podría ser mejor.

El problema es que no sabemos cuando puede salir la versión de geolocalización ni tampoco la de departamentos destacados. Necesitamos que convivan. En mi estrategia lo haría de la siguiente manera:

Es importante aclarar que a cada versión le puse una letra “a” y “b”. Porque más a allá de que sea otro release, pertenece a la instancia donde se va a generar una nueva versión para producción, la cual seguirá siendo la 1.1.0.

Podrían llamarla 1.1.0 a las dos sin ningún problema, pero le puse letras para evitar confusiones. Es importante que nuestro nuevo release paralelo salga desde master.

Supongamos que la empresa se decide por publicar primero geolocalización. Por lo tanto, si nos queda algun feature pendiente, es mejor cerrarlo e integrar todo a master:

En Master nos queda la versión 1.1.1 porque:

  • Generamos un release con 1.1.*
  • Generamos un feature 1.1.1
  • Si hubieramos generado mas features, tendríamos que mantenernos siempre incrementando el último número de versión. Por ende, si generamos 10 features, tendríamos la versión 1.1.10

Mientras seguimos trabajando con el release de departamentos destacados, surge un bug crítico en producción. Entonces creamos un bugfix que sale desde master, lo resolvemos y generamos una versión usando también el último número:

Y finalmente cuando terminamos de trabajar con el release de destacados, quizá lo integremos o podemos eliminarlo, dejando master en su última versión aprobada. Supongamos que si aprobamos ese release:

Como ven, el tercer número vuelve a cero porque tenemos un nuevo release, y esto implica que ese dígito pertenece a todo lo que implique departamentos destacados y geolocalización, porque lo integramos con producción.

Mi estrategia debe tener muchos puntos criticables, así como las demás que me crucé cuando investigaba esto. Pero en los casos donde la apliqué, me pareció bastante práctica. Si llegan a aplicarla, dejen su opinión en los comentarios.

Nos vemos en el siguiente artículo!

Si te gustan los artículos que escribo y querés apoyarme con el proyecto, podés hacerlo mediante Patreon 🙂

¡Deja un comentario!

Artículos relacionados

Curso Kotlin | #19. Pair

A veces necesitamos relacionar dos valores y almacenarlos en una variable única, y para ello tenemos a Pair.

Curso Kotlin | #17. Proyecto: The Hero Legacy

Si empezaste esta serie desde cero y ya leíste 16 capítulos, ¡Felicidades! Empezaste a dominar los primeros conceptos fundamentales en Kotlin. Este es el primer paso de tu senda como desarrollador/a. Estamos en una instancia en donde podemos poner en práctica todo lo que aprendiste hasta ahora. Bienvenido/a al primer proyecto de la serie.

Curso Kotlin | #16. Funciones

Las funciones son procedimientos que se pueden reutilizar y nos permiten encapsular comportamientos y mejorar la lectura del código.

Curso Android | #4. Emuladores

En este capítulo, vamos a configurar un emulador y ver alternativas al emulador de Android Studio. Podés acceder al curso completo desde este link. También