Entrega continua con la potencia extra de Git

Ahora que Git ha acabado con los problemas de la fusión, los flujos de trabajo de ramificación son mucho más atractivos.

Sarah Goff-Dupont Sarah Goff-Dupont

Todos hemos oído decir que hay que tener "cuidado con el código escrito por una sola persona" y conocemos las ventajas de hacer software en equipo, ya que se combinan diferentes formas de pensar, formaciones y experiencias... Y, al reunir esas diferencias para resolver cualquier problema, el software termina siendo mejor. Es más fácil de mantener, de mayor calidad y, en última instancia, mejor para el usuario.

Colaboración en equipo | CI/CD de Atlassian

Pero también sabemos otra cosa: ¡el desarrollo en equipo puede ser un caos!

Hay que saber en qué fragmentos de código está trabajando cada uno, asegurarse de que los cambios no entren en conflicto, adelantarse a los clientes en la detección de errores y mantener a todos los que participan en el proyecto a la última de los progresos. Lo cierto es que todos estos problemas pueden abordarse o con las ramas de Git o con la entrega continua.

Mi intención es mostrar que, al combinar estas dos estrategias (y, si te apetece, incluir alguna herramienta más a la receta), tendrás una fórmula de potencia extra para el éxito.

La eficacia de los flujos de trabajo basados en ramas 

A decir verdad, no es que Git sea de por sí perfecto para la entrega continua, sino que los flujos de trabajo de ramificación son idóneos para CD, y Git es idóneo para los flujos de trabajo de ramificación. Además de ser el complemento perfecto para CD, los flujos de trabajo de ramas y fusiones permiten resolver errores espinosos, probar nuevas tecnologías o, sencillamente, programar una nueva función desde cero sin el riesgo de que tus cambios impidan que el resto del equipo avance con sus propias tareas.

Diagrama de flujo de trabajo básico | CI/CD de Atlassian

Subversion y otros sistemas tradicionales de control de versiones también permiten trabajar con ramas, está claro. Sin embargo, en este punto vamos a conocer al gemelo malvado de la ramificación: la fusión.

Los sistemas tradicionales de control de versiones como Subversion no son eficaces a la hora de hacer un seguimiento de versiones de archivos de diferentes ramas y, cuando llega el momento de fusionar, Subversion tiene que hacer un alto y pedir muchas indicaciones. (Ya sabes... esa ventanita emergente que te pregunta: "¿Quieres tener esta línea o aquella en la versión fusionada?"). Al hacer falta tanta intervención humana durante las fusiones, los equipos tienden a congelar código para que, al hacer la fusión, no les interrumpan los cambios que entran en una rama. Y congelar el código resulta caro, porque es un tiempo bastante improductivo.

Git, por otro lado, es muy bueno en el seguimiento de cambios en diferentes versiones de archivos de diferentes ramas, y siempre sabe cómo era el antepasado común de cada archivo. Es como si tuviera un GPS integrado que le permite navegar por fusiones sin tener que parar a pedir indicaciones todo el tiempo.

Con Git, puedes sacar todo el partido de la ramificación de una manera impensable con Subversion. La carga de trabajo ligada al trabajo con ramas y fusiones es tan pequeña que crear ramas para solo un par de días no solo es factible, sino práctico.

Vale, de acuerdo. Pero, ¿por qué es tan eficaz crear ramas para la entrega continua?

Con las ramas, las ramas principales se mantienen limpias y listas para publicar

Como hemos visto, las ramas de corta duración son una forma excelente para que los desarrolladores colaboren en un proyecto o función sin poner trabas a los demás. Además, y más importante en relación con la CD, al aislar el trabajo en curso en ramas de desarrollo, la rama principal y las ramas de versiones estables se mantienen limpias y se pueden lanzar en cualquier momento.

Esto significa ejecutar toda la batería de pruebas automatizadas en las ramas de desarrollo, para que los desarrolladores tengan información útil sobre la calidad de su código y puedan decidir con fundamento cuándo se pueden fusionar los cambios. (Si aún no haces pruebas automatizadas, consulta esta entrada de RebelLabs, con consejos e información para elaborar tus primeras pruebas unitarias).

También significa usar las solicitudes de incorporación de cambios de Git como una forma de revisión de código para que así todo el equipo confíe en la capacidad de mantenimiento e interoperabilidad del código con otras áreas del código base. Sí, supone más trabajo inicial que los modelos de entrega tradicionales. Y sí, merece la pena.

Para una buena entrega continua, las ramas de publicación deben estar siempre limpias.

Por ejemplo, todos los equipos de desarrollo de Atlassian tienen acordado que nada se introduce mediante confirmación directamente en la rama principal ni en las ramas estables. Todo el trabajo se hace en ramas. De hecho, somos tan fanáticos de las ramas que hemos optado por crear una rama independiente para cada incidencia de Jira que abordamos (luego veremos esto).

De esta forma, ¡la gente puede romper tantas pruebas y hacerles tanto daño a sus ramas como quiera! La rama principal siempre está lista para la publicación y permite crear nuevas ramas sin heredar un montón de código roto. Eso es todo un plus para la productividad de los desarrolladores en CD y en general (por no hablar de lo positivamente que afecta a su estado de ánimo).

Las ramas ayudan a aceptar contribuciones externas al equipo

Con la capacidad de ramificación de Git (especialmente la capacidad de bifurcar repositorios completos), es muy sencillo incorporar contribuciones de personas externas al equipo: contratistas, desarrolladores de empresas asociadas, desarrolladores de otras unidades empresariales, etc. Ya no te quitará el sueño tener a personas que no están familiarizadas con tu código base haciendo cambios en ramas críticas y desbaratando tu capacidad de lanzar código nuevo.

Como en tantas otras cosas, unas rigurosas pruebas automatizadas en ramas o repositorios bifurcados son la clave para una buena colaboración. Antes de aprobar una fusión en la línea de código principal, querrás revisar los resultados de las pruebas y la compilación.

Consejo profesional: Los gestores de repositorios como Bitbucket permiten usar hooks de Git para garantizar el cumplimiento de los estándares de calidad. Por ejemplo, puedes establecer una regla que deben cumplir todas las compilaciones de rama para aceptar y fusionar una solicitud de incorporación de cambios.

Una buena ramificación da claridad para el seguimiento del proyecto

Ahora está de moda crear una rama de desarrollo para cada historia, corrección de errores o tarea (por ejemplo, cada incidencia de Jira) que se implementa. En Atlassian, adoptamos este modelo de rama por incidencia hace ya un par de años, ¡y no nos hemos arrepentido! Lo que es más, también se ha hecho muy popular entre nuestros clientes.

Al crear una rama para cada incidencia, es muy sencillo elegir a mano los cambios que hay que lanzar a la producción o agrupar en una publicación. Al no acumular una gran cantidad de cambios en la rama principal, se puede seleccionar lo que se incorpora a esta y cuándo se hace. Puedes lanzar el MVP de un epic y un cambio "deseable", en lugar de esperar a que todos los cambios "deseables" estén terminados. Si lo prefieres, también puedes lanzar una sola corrección de errores y hacerlo en el marco de trabajo de una publicación habitual. Aunque la corrección sea urgente, para sacar los cambios no tendrás que hacer malabares para respaldar todos los que todavía no estén listos para el lanzamiento.

Esa facilidad de lanzar un solo cambio de código es la esencia de entrega continua.

Este concepto mantiene el código no probado fuera de la rama principal. Además, si incluyes el nombre o las iniciales del desarrollador o la clave de la incidencia de Jira en el nombre de la rama, podrás conocer el estado de desarrollo de cada incidencia en curso.

Captura de pantalla del repositorio de Git, confirmaciones Bitbucket | CI/CD de Atlassian

Sigue la convención de nomenclatura de la imagen anterior: es la clave única de la incidencia de Jira que se está implementando y una descripción breve y legible de la incidencia. Un gestor de publicaciones o cualquier otro interesado podría ver el repositorio de arriba y saber de un vistazo que la historia de usuario rastreada por AMKT-13952 está lista para publicar, ya que se ha fusionado con la rama principal. Esto sí que es trazabilidad sin esfuerzo manual.

Entonces, ¿cómo funcionan Git y el flujo de trabajo de entrega continua?

¡Buena pregunta! Aquí voy a explicarlo por encima, ya que hay otros artículos se ocupan con detalle de cada fase.

  • Crea una rama para la incidencia en la que vas a trabajar. Incluye la clave de la incidencia de Jira en el nombre de la rama para que quede claro para qué sirve. Además, si utilizas otras herramientas de Atlassian como Bitbucket o Bamboo, recogerán esa clave y crearán enlaces entre la incidencia, la rama, todas las confirmaciones, compilaciones, solicitudes de incorporación de cambios e implementaciones relacionadas con la incidencia. En otras palabras, las claves de incidencia son mágicas.
  • Haz los cambios en la rama. Este es tu mundo, así que haz lo que te plazca. Prueba cosas nuevas o rompe lo que sea. No importa, porque luego…
  • Pon tu rama en CI. (Bamboo lo hará por ti automáticamente, por cierto.) Tu equipo y tú deberéis decidir si ejecutar pruebas especializadas como pruebas de carga o pruebas integrales basadas en IU, y si desencadenar automáticamente una ejecución de prueba cada vez que se envíen cambios a la rama. Los equipos de Atlassian suelen ejecutar pruebas a nivel de unidad o de integración en las ramas de desarrollo, y dejan que el desarrollador elija con qué frecuencia se ejecutarán. Así se ahorran recursos de compilación y se evitan obstrucciones innecesarias en la cola.
  • Actualiza la rama con los últimos cambios de la rama principal de manera frecuente. Puedes hacerlo fusionando mediante cambio de base o con el procedimiento de confirmación de rama. Tú decides. Eso sí, recuerda no fusionar mediante cambio de base una rama que compartas con otros desarrolladores: no les hará mucha gracia. De cualquier manera, detectarás conflictos de integración antes de la fusión, lo que también te servirá para mantener limpia la rama principal.
  • Crea una solicitud de incorporación de cambios cuando esté todo listo para la fusión. Esto significa que la implementación ha terminado, que has incorporado cambios de tus compañeros de equipo, que has resuelto los conflictos resultantes y que se han superado todas las pruebas en la rama.
  • Fusiona e implementa como te apetezca. Algunos equipos prefieren lanzar automáticamente cada cambio en cuanto se fusiona con la rama principal y se superan todas las pruebas; este es el modelo de implementación continua. Otros equipos prefieren que la decisión sobre qué cambios se deben lanzar y cuándo sea humana. La elección es tuya y de tu equipo.

Automatización de CI de Git | CI/CD de Atlassian

Ahora toca tomarla. Con los flujos de trabajo de ramificación, la entrega continua es una propuesta más sencilla y Git acaba con los dolores de cabeza de la ramificación. Sigue leyendo y aprende a configurar tu repositorio de Git para que facilite la CD y a poner todas estas ideas en práctica con las herramientas de Atlassian. ¡Nos vemos en la siguiente página!