La verdadera diferencia entre la integración continua y la implementación continua

Hay mucho contenido que describe qué son la integración continua, la entrega continua y la implementación continua. Pero, ¿para qué sirven estos procesos en primer lugar?

Es fundamental comprender los problemas que CI y CD resuelven para utilizarlos correctamente. Esto le permitirá a su equipo mejorar su proceso y evitar esforzarse en perseguir métricas sofisticadas que no aportan ningún valor a su proceso.

La integración continua es un problema de equipo

Si trabaja en equipo, es probable que haya varios desarrolladores trabajando en el mismo repositorio. Hay una rama principal en el repositorio que contiene la última versión del código. Los desarrolladores trabajan en diferentes cosas en diferentes ramas. Una vez que alguien haya terminado con su cambio, lo presionará o fusionará con la rama principal. Eventualmente todo el equipo logrará este cambio.

El escenario que queremos evitar es que una confirmación defectuosa llegue a la rama principal. Defectuoso significa que el código no se compila o que la aplicación no se inicia o no se puede utilizar. ¿Por qué? No porque la aplicación esté rota o porque todas las pruebas deban ser siempre verdes. Eso no es un problema; puede decidir no implementar esa versión y esperar una solución.

El problema es que todo tu equipo está atascado. Todos los desarrolladores que sacaron el compromiso defectuoso pasarán 5 minutos preguntándose por qué no funciona. Probablemente varios intentarán encontrar el compromiso defectuoso. Algunos intentarán solucionar el problema por sí mismos en paralelo al autor del código defectuoso.

Es una pérdida de tiempo para su equipo. La peor parte es que los incidentes repetidos alimentan la desconfianza de la rama principal y animan a los desarrolladores a trabajar por separado.

La integración continua se trata de evitar que la rama principal se rompa para que su equipo no se atasque. Eso es. No se trata de tener todas sus pruebas en verde todo el tiempo y la rama principal implementable en producción en cada confirmación.

El proceso de Integración Continua es independiente de cualquier herramienta. Puede verificar manualmente que la combinación de su rama y la rama principal funciona localmente, y luego solo enviar la combinación al repositorio. Pero eso sería muy ineficiente. Es por eso que la Integración Continua se implementa mediante verificaciones automatizadas.

Los controles garantizan que, como mínimo:

  • La aplicación debería compilarse y comenzar
  • La mayoría de las características críticas deben ser funcionales en todo momento (registro de usuario / recorrido de inicio de sesión y características comerciales clave)
  • Las capas comunes de la aplicación en las que confían todos los desarrolladores deben ser estables. Esto significa pruebas unitarias en esas partes.

En la práctica, esto significa que debe extraer cualquier marco de prueba unitario que funcione para usted y asegurar las capas comunes de la aplicación. A veces no es tanto código y se puede hacer con bastante rapidez. También debe agregar una "prueba de humo" para verificar que el código se compila y que la aplicación se inicia. Esto es especialmente importante en tecnologías con locas inyecciones de dependencia como Java Spring o .NET core. En proyectos grandes, es tan fácil conectar incorrectamente sus dependencias que es imprescindible verificar que la aplicación siempre se inicia.

Si tiene cientos o miles de pruebas, no necesita ejecutarlas todas para cada combinación. Llevará mucho tiempo y la mayoría de las pruebas probablemente verifiquen las características de "no bloqueadores de equipo".

Veremos en las próximas secciones cómo el proceso de Entrega Continua hará un buen uso de estas muchas pruebas.

No se trata de herramientas

Las herramientas y los controles automáticos están bien. Pero si sus desarrolladores solo fusionan ramas gigantes en las que trabajan durante semanas, no lo ayudarán. El equipo pasará una buena cantidad de tiempo fusionando las ramas y arreglando las incompatibilidades de código que surgirán eventualmente. Es una pérdida de tiempo tanto como estar bloqueado por un compromiso defectuoso.

La integración continua no se trata de herramientas. Se trata de trabajar en pequeños fragmentos e integrar su nuevo código en la rama principal y extraerlo con frecuencia.

Con frecuencia significa al menos diariamente. Divida la tarea en la que está trabajando en tareas más pequeñas. Fusiona tu código con mucha frecuencia y tira con mucha frecuencia. De esta forma nadie trabaja aparte durante más de uno o dos días y los problemas no tienen tiempo de convertirse en bolas de nieve.

No es necesario que una gran tarea esté en una sola rama. Nunca debería serlo. Las técnicas para fusionar el trabajo en curso con la rama principal se denominan "ramificación por abstracción" y "alternancia de funciones". Consulte la publicación del blog Cómo comenzar con la integración continua para obtener más detalles.

Puntos clave para una buena construcción de CI

Es muy simple. Que sea breve. 3-7 minutos debe ser el máximo. No se trata de CPU y recursos. Se trata de la productividad de los desarrolladores. La primera regla de la productividad es el enfoque. Haga una cosa, termínela y luego pase a la siguiente.

El cambio de contexto es costoso. Los estudios demuestran que se necesitan ~ 23 minutos para volver a concentrarse profundamente en algo cuando te molestan.

Imagina que empujas tu rama para fusionarla. Empiezas otra tarea. Pasas de 15 a 20 minutos metiéndote en él. El minuto después de que esté en la zona, recibirá una notificación de "error de compilación" de su compilación de CI de 20 minutos de duración para la tarea anterior. Vuelve a arreglarlo. Lo empuja de nuevo. Perdiste fácilmente más de 20 minutos yendo y viniendo.

Multiplique 20 minutos una o dos veces al día por el número de desarrolladores de su equipo ... Eso es mucho tiempo precioso perdido.

Ahora imagínese si la respuesta llegara en 3 minutos. Probablemente no habría comenzado la nueva tarea en absoluto. Debería haber leído su código una vez más o revisado un PR mientras espera. La notificación fallida vendría y la arreglarías. Luego, podría pasar a la siguiente tarea. Ese es el tipo de enfoque que debería permitir su proceso.

Mantener su construcción de CI corta lo convierte en una compensación. Las pruebas que se ejecutan durante más tiempo o proporcionan poco valor en el contexto de CI deben trasladarse al paso de CD. Y sí, las fallas también deben corregirse. Pero como no impiden que nadie haga lo suyo, puede tomar las correcciones como una "próxima tarea" cuando termine lo que está haciendo. Simplemente apague las notificaciones mientras trabaja y verifique de vez en cuando. Mantenga el cambio de contexto al mínimo.

La entrega y el despliegue continuos son problemas de ingeniería

Establezcamos las definiciones para sacar eso del camino.

Continuous Delivery se trata de poder implementar cualquier versión de su código en todo momento. En la práctica, significa la última o anterior versión de su código. No se implementa automáticamente, generalmente porque no tiene que hacerlo o está limitado por el ciclo de vida de su proyecto. Pero tan pronto como alguien lo desee, se puede realizar una implementación en una cantidad mínima de tiempo. Ese alguien puede ser el equipo de prueba / control de calidad que quiera probar cosas en un entorno de prueba o preproducción. O puede ser el momento de implementar el código en producción.

La idea de Continuous Delivery es preparar los artefactos lo más cerca posible de lo que desea ejecutar en su entorno. Estos pueden ser archivos .jar o .war si está trabajando con Java, o ejecutables si está trabajando con .NET. Estas también pueden ser carpetas de código JS transpilado o incluso contenedores Docker, lo que haga que la implementación sea más corta (es decir, ha preconstruido tanto como pueda por adelantado).

Al preparar artefactos, no me refiero a convertir el código en artefactos. Suelen ser unos pocos scripts y unos minutos de ejecución. Preparar significa:

Ejecute todas las pruebas que pueda para asegurarse de que, una vez implementado, el código realmente funcione. Ejecute pruebas unitarias, pruebas de integración, pruebas de un extremo a otro e incluso pruebas de rendimiento si puede automatizarlo.

De esta manera, puede filtrar qué versiones de su rama principal están realmente listas para producción y cuáles no. La suite de pruebas ideal:

  • Asegura que las funcionalidades clave de la aplicación funcionen. Idealmente todas las funcionalidades
  • Garantiza que no se haya introducido ningún factor decisivo de rendimiento, de modo que cuando su nueva versión llegue a sus numerosos usuarios, tenga la oportunidad de durar
  • Ejecute cualquier actualización de la base de datos que necesite su código para evitar sorpresas

No es necesario que sea muy rápido. 30 minutos o 1 hora es aceptable.

La implementación continua es el siguiente paso. Implementa la versión más actualizada y lista para producción de su código en algún entorno. Lo ideal es producción si confía lo suficiente en su suite de pruebas de CD.

Tenga en cuenta que, según el contexto, esto no siempre es posible o vale la pena el esfuerzo. La entrega continua a menudo es suficiente para ser productivo, especialmente si trabaja en una red cercana y tiene entornos limitados en los que puede implementar. También puede ser que el ciclo de lanzamiento de su software evite implementaciones no planificadas.

La entrega continua y la implementación continua (llamémosles CD de ahora en adelante) no son problemas de equipo. Se trata de encontrar el equilibrio adecuado entre el tiempo de ejecución, los esfuerzos de mantenimiento y la relevancia de su conjunto de pruebas para poder decir "Esta versión funciona como debería".

Y es un equilibrio. Si sus pruebas duran 30 horas, eso es un problema. Vea esta publicación épica sobre cómo se ve el conjunto de pruebas de la base de datos de Oracle. Pero si pasa tanto tiempo manteniendo sus pruebas actualizadas con el último código que impide el progreso del equipo, eso tampoco es bueno. Además, si su suite de pruebas no garantiza prácticamente nada ... es básicamente inútil.

En un mundo ideal, queremos un conjunto de artefactos desplegables por compromiso en la rama principal. Puede ver que tenemos un problema de escalabilidad vertical: cuanto más rápido pasamos del código a los artefactos, más preparados estamos para implementar la versión más reciente del código.

Cual es la gran diferencia?

La integración continua es un problema de escalabilidad horizontal. Desea que los desarrolladores fusionen su código con frecuencia, por lo que las comprobaciones deben ser rápidas. Idealmente, en cuestión de minutos para evitar que los desarrolladores cambien de contexto todo el tiempo con comentarios altamente asincrónicos de las compilaciones de CI.

Cuantos más desarrolladores tenga, más potencia informática necesitará para ejecutar comprobaciones simples (compilación y prueba) en todas las ramas activas.

Una buena compilación de CI: garantiza que no se introduzca en la rama principal ningún código que rompa cosas básicas y evite que otros miembros del equipo trabajen, y es lo suficientemente rápido como para proporcionar comentarios a los desarrolladores en minutos para evitar el cambio de contexto entre tareas.

La entrega e implementación continuas son problemas de escalabilidad vertical. Tiene que realizar una operación bastante compleja.

Una buena compilación de CD: garantiza que tantas funciones como sea posible funcionen correctamente. Cuanto más rápido, mejor, pero no es una cuestión de velocidad. Una compilación de 30 a 60 minutos está bien.

Un error común es ver el CD como un problema de escalabilidad horizontal como CI: cuanto más rápido pueda pasar del código a los artefactos, más confirmaciones podrá procesar y más cerca podrá estar del escenario ideal.

Pero no lo necesitamos. Producir artefactos para cada confirmación y lo más rápido posible suele ser una exageración. Puede acercarse al CD con el mejor esfuerzo posible: tenga una sola compilación de CD que solo seleccionará la última confirmación para verificar una vez que finalice una compilación determinada.

No se equivoque sobre el CD. Es muy dificil. Tener suficiente confianza en las pruebas para decir que su software está listo para implementarse automáticamente generalmente funciona en aplicaciones de poca superficie como API o IU simples. Es muy difícil de lograr en una interfaz de usuario compleja o en un sistema monolítico grande.

Conclusión

Las herramientas y los principios utilizados para ejecutar CI y CD suelen ser muy similares. Sin embargo, los objetivos son muy diferentes.

La integración continua es una compensación entre la velocidad del ciclo de retroalimentación para los desarrolladores y la relevancia de las comprobaciones que realiza (compilación y prueba). Ningún código que impida el progreso del equipo debe llegar a la rama principal.

La entrega continua de implementación consiste en ejecutar comprobaciones lo más exhaustivas posible para detectar problemas en su código. La integridad de los controles es el factor más importante. Por lo general, se mide en términos de cobertura de código o cobertura funcional de sus pruebas. La detección temprana de errores evita que el código roto se implemente en cualquier entorno y ahorra el valioso tiempo de su equipo de prueba.

Elabore sus compilaciones de CI y CD para lograr estos objetivos y mantener la productividad de su equipo. Ningún flujo de trabajo es perfecto. Surgirán problemas de vez en cuando. Úselos como lecciones aprendidas para fortalecer su flujo de trabajo cada vez que lo hagan.

Publicado el 27 de noviembre de 2019 en el blog Fire CI.