CI sigue el principio de que si algo cuesta mucho esfuerzo, se debe hacer más a menudo para que sea menos doloroso.
Un sistema software es algo muy complejo. Un cambio aparentemente sencillo en un fichero puede tener efectos no deseados en el sistema. Cuando muchos desarrolladores trabajan en un grupo de sistemas relacionados, coordinar los cambios es difícil, porque los cambios de diferentes desarrolladores pueden ser incompatibles.
Las prácticas de integración continua (CI) sirven para abordar estos problemas.
- CI propone crear ciclos de retroalimentación rápidos para garantizar que los desarrolladores trabajen en lotes pequeños.
- CI permite a los equipos producir software de calidad, reducir el coste de desarrollo y mantenimiento, y aumentar la productividad.
- SCM, CVS, configuration management,...
- Poner en el repo todo lo necesario para hacer un build desde cero: código, test scripts, properties files, database schema, install scripts, third party libraries,... incluso compiladores (!)
- No poner en el repo los productos de un build, solo los scripts para hacerlo
- Antes de hacer un checkout para el build desde cero, quizá solo debería tener que instalarse un SO, un entorno de desarrollo (!) y un SGBD. A veces ni eso.
- Minimizar el número de ramas
- No es imprescindible hacer TDD o XP
- Pero hay que tener una suite de tests automatizados. Si falla uno, debe fallar el build
- Empezar con XUnit y seguir con pruebas de interfaz de usuario (Selenium), APIs (Appium), mocking (Mockito), etc.
- Integrar el análisis estático de código (SonarQube, ESLint)
- Para arreglar pronto los problemas, hay que encontrarlos pronto. Hacer commit frecuentes ayuda.
- Si se trabaja en una rama, hay que hacer merge con frecuencia con el trunk
- También se detectan conflictos al ejecutar el build: hacer diff debugging (hacer checkout de código entre un par de fechas, averiguar cuándo se introdujo el cambio que provoca el fallo y hacer diff para ver qué ha cambiado)
Primero hay que proporcionarle un commit bueno y uno malo
Mensaje que dice cuántos pasos quedan hasta encontrar el commit malo
Bisecting: X revisions left to test after this (roughly Y steps)
Repetir en cada paso indicando si el bug aún persiste
- Si el bug persiste, git bisect bad
- Si el bug desaparece, git bisect good
Cuando se completan todos los pasos, git muestra el mensaje con el SHA del primer commit malo
Tras encontrar el commit que introdujo el bug, se puede resetear el git bisect
Alguien puede no hacer un update y build local antes de hacer commit. Los desarrolladores pueden tener configuraciones diferentes en sus máquinas, así que hay que hacer los build en una máquina compartida
En el build manual el desarrollador se conecta y lanza el build.
El servidor de integración monitoriza el repositorio, lanza el build cuando hay un commit y notifica al desarrollador
Servidores de CI (algunos solo disponibles en la nube)
Nightly builds no es hacer CI
Prioridad 1: arreglar un build que falla
No todo el mundo tiene que dejar de hacer lo que está haciendo para arreglarlo. Con un par de personas suele ser suficiente. Para poder hacer esto hay que seguir un workflow que lo permita.
Manera rápida: revertir el commit más reciente que ha roto el build y hacer debug en local
Técnica pending-head para evitar romper el mainline: crear una working copy que se actualiza desde el head verdadero (para mantenerse sincronizado) pero hacer commits en una rama diferente pending-head.
El cuello de botella más habitual es el testing (en particular, si involucran servicios externos como bases de datos): mocking!
Deployment pipeline, aka build pipeline / staged build
Ejemplo two-stage pipeline: 1º rápida (compilación y pruebas unitarias sin la BD), 2º lenta (pruebas de integración con la BD real). El primer commit build se hace tras la 1ª etapa. Si falla el build secundario tras la 2ª etapa, es un síntoma de que hacen falta más tests en los commit builds.
La CI también incluye dos prácticas más, según Kent Beck y la comunidad XP:
1. TBD: los desarrolladores trabajan sobre el trunk (= master, main o mainline) en pequeños lotes y fusionan su trabajo regularmente en un trunk compartido, al menos una vez al día, en lugar de trabajar en ramas de features de larga duración.
2. TDD: La creación de suites de pruebas unitarias automatizadas mantenibles es compleja. Una manera de resolver este problema es practicar el TDD. Los desarrolladores escriben pruebas automatizadas que inicialmente fallan, antes de implementar el código que hace que las pruebas pasen.
Las prácticas de CI se consideran a veces controvertidas.
- Requiere que los desarrolladores dividan las características grandes y otros cambios en pasos incrementales más pequeños que se puedan integrar con frecuencia en el trunk. Esto es un cambio para los desarrolladores que no están acostumbrados a trabajar de esta manera.
- Además, cuando los equipos cambian a usar pasos pequeños, puede llevar más tiempo completar las características grandes.
Cuando los cambios son en lotes pequeños (y autocontenidos):
- El proceso de CI da como resultado un desarrollo y entrega más rápidos y estables
- Las ramas en las que viven los cambios son de corta duración
- También garantiza que los desarrolladores reciban comentarios periódicos sobre el impacto de su trabajo en el sistema en su conjunto, tanto de otros desarrolladores, probadores y clientes, como de las pruebas automatizadas de rendimiento y seguridad.
- Esto hace más fácil y rápido detectar, clasificar y solucionar problemas.
A pesar de las objeciones, ayudar a los equipos de desarrollo de software a implementar la CI debería ser la prioridad número uno para comenzar el viaje hacia la CD.
Arquitectura poco acoplada: permite a los equipos probar y desplegar sus aplicaciones de forma independiente, sin necesidad de orquestación con otros servicios. Permite trabajar de forma independiente sin depender de otros equipos para obtener soporte y servicios.
Gestión de cambios en la base de datos: almacenar los cambios de la BD como scripts en el control de versiones (y gestionar estos cambios de la misma manera que los cambios de la aplicación en producción)
Al principio de la curva de transformación se logran victorias rápidas.
En una etapa inicial de mejora, la automatización ayuda a progresar de un bajo rendimiento a un rendimiento medio.
En el punto más bajo de la curva, la automatización aumenta los requisitos de prueba, que se tratan manualmente. La gran cantidad de deuda técnica bloquea el progreso.
Al salir de la curva, la deuda técnica y el incremento de complejidad ralentizan el trabajo, provocando añadir controles manuales y más capas de procesos tras cada cambio.
Sólo en la parte alta de la curva, el trabajo de mejora realizado logra un rendimiento alto.