Siempre he pensado que las herramientas que usamos en el desarrollo de software deben estar seleccionadas y adaptadas en función del tipo de desarrollo que realizamos, el equipo, la metodología, etc.
Sin embargo, hay tres herramientas básicas que uso en todos los proyectos: un sistema de control de versiones (desde git hasta subversion), un gestor de bugs/tareas (aunque sea una hoja Excel) y un servidor de integración continua.
Las dos primeras herramientas de la lista están muy extendidas y no conozco ningún equipo de desarrollo que trabaje sin ellas, pero lo del servidor de integración continua es menos conocido y no se usa tanto.
¿Qué es la integración continua?
La integración continua es una practica ligada a la programación extrema que consiste en automatizar el proceso de integración de código para poder realizarlo de la manera más continuada posible.
Cuando llega el momento de liberar una nueva versión de una aplicación hay que realizar un proceso de integración de todos los componentes que forman parte de la aplicación. Esto implica realizar varios pasos, como obtener el código fuente, compilarlo, ejecutar los tests, etc.
En muchos proyectos este es un proceso completamente manual, en el mejor de los casos documentado en forma de checklist en alguna parte, pero a fin de cuentas, siempre que haya pasos manuales, antes o después habrá algún error y pondremos en producción una aplicación con el número de versión incorrecto, que no ha pasado las pruebas automatizadas o que no estará correctamente etiquetada en el control de código fuente.
Si queremos mejorar este proceso, el primer paso es automatizarlo. Para ello contamos con diferentes herramientas, generalmente basadas en algún tipo de script de compilación. En .NET se suele utilizar MSBuild, en Javascript hay opciones como Grunt, en Ruby existe Rake… Al final todas se basan en lo mismo, poder definir de una manera más o menos cómoda las tareas a ejecutar para poder lanzarlas en un único paso.
Un ejemplo de script de compilación para una aplicación de escritorio podría contar con los siguientes pasos:
- Obtener la última versión del código fuente.
- Actualizar la información de versión en el fichero AssemblyInfo.cs basándose en la convención que se haya establecido (número de compilación, fecha, etc.).
- Ejecutar una herramienta de análisis estático como FxCop o SourceMonitor para detectar defectos en el código.
- Compilar los proyectos.
- Ejecutar las suites de tests automatizados.
- Generar la documentación (manuales, guías de instalación, etc.).
- Crear los paquetes de instalación.
- Etiquetar en el control de código fuente la versión del código usada para generar estos instaladores, de forma que luego podamos trazar exactamente qué codigo fuente se uso para generar cada versión de la aplicación.
Preparar este script puede llevar un tiempo y no es una tarea fácil la primera vez que lo haces, pero una vez que has aprendido es bastante sencillo adaptarlo para nuevos proyectos, por lo que la inversión de tiempo se recupera rápidamente.
¿Qué aporta un servidor de integración continua a este proceso?
Una vez que contamos con este script, cada vez que queramos realizar una integración completa (potencialmente liberable) del código, podremos hacerlo en un sólo paso ejecutándolo desde cualquier equipo; entonces, ¿para qué hace falta un servidor de integración continua?
El servidor de integración continua se encarga de ejecutar este script automáticamente según la programación que establezcamos, por ejemplo cada vez que se se realiza un commit/checkin/push al control de código fuente.
Además, podemos establecer distintas configuraciones para un mismo proyecto. Por ejemplo, podemos crear una configuración que realice una compilación «rápida», ejecutando sólo los test unitarios cada vez que realizamos un commit, y programar una compilación «completa», que ejecuta también los tests de integración y genera los instaladores todas las noches (las típicas nightly builds).
Realizar el proceso de integración de continuamente en un servidor tiene varias ventajas:
Por una parte, evita discrepancias entre las versiones utilizadas para generar los entregables. Al realizar la compilación en el servidor se garantiza que siempre se usa la misma versión del compilador, SDKs, librerías de terceros, etc. Eso es difícil de conseguir si la compilación se realiza cada vez en equipo distintos donde podría haber distintas versiones de cosas instaladas globalmente (assemblies en el GAC, módulos node instalados con npm install -g
, etc.), provocando cambios en el comportamiento final de la aplicación dependiendo del equipo en que se ha compilado. Este tipo de errores son complicados de diagnosticar y pueden hacerte perder mucho tiempo.
Además, permite detectar rápidamente errores introducidos por cambios en el código. Aunque se supone que antes de hacer un commit cada desarrollador debería asegurarse de que todo funciona y ejecutar los tests en su equipo, a veces las suites de tests tardan más tiempo del que nos gustaría en ejecutarse (especialmente si tenemos tests de integración) o directamente al desarrollador se le olvida. Al hacer que el código se compile cada vez que se hace el commit en el servidor de integración continua, los errores se detectan en cuestión de minutos en lugar de hacerlo varios días después cuando alguien intenta liberar una nueva versión.
Otra ventaja de usar un servidor de integración continua es que podemos mantener un histórico de los resultados de las compilaciones. Dependiendo de los análisis que estemos realizando sobre nuestro código, esto nos permite conocer como ha evolucionado la cobertura de código de los tests, la complejidad ciclomática media y todas esas métricas que tanto nos gustan (otra cosa es que sirvan para algo, pero eso lo dejamos para otro día).
En teoría, si este proceso se lleva al extremo cada vez que se hace un commit al control de código fuente el servidor de integración continua sería capaz de detectar si todo es correcto y en ese caso hacer un despliegue automático. Eso es lo que se conoce como entrega continua (continuous delivery o continuous deployment) y hay proyectos que son capaces de hacerlo, aunque no siempre es viable (ni recomendable).
Conclusiones
Montar un servidor de integración continua puede resultar un poco complicado al principio. Hay muchas opciones disponibles, algunas más sencillas y otras más complejas, pero donde realmente reside la complicación es en estructurar tu proyecto para que todas las tareas sean fácilmente automatizables.
Aunque pueda parecer que este tipo de técnicas sólo son aplicables a equipos muy grandes que hacen aplicaciones muy complejas, no es así. Yo lo he utilizado en proyecto con un único desarrollador en los que lo único que se hacía era compilar el código y generar un instalador, y aun así merece la pena. Tener la seguridad de que lo que has compilado es lo que se debía compilar, que la versión está correctamente asignada y que el instalador lo has generado con los binarios correctos (y no con otros que compilaste dos horas atrás), te permite vivir mucho más tranquilo.
Es verdad que el coste inicial es alto, pero lo bueno es que las técnicas necesarias son muy fáciles de reutilizar de un proyecto a otro, por lo que una vez que has automatizado la integración del primer proyecto, si sigues una estructura común en todos tus proyectos, el resto es cuestión de ajustar un par de rutas en los scripts de compilación y los tienes listos en minutos (literalmente).
Me ha encantado tu post! Entre esto y el vago recuerdo que aun tengo debido al exceso de cerveza del gusenet, me doy por enteradísimo de que es y por que debo utilizar un servidor de CI.
Muchas gracias por el detalle de escribir el post! :-)
Muy bueno, este es un tema muy interesante del que no hay mucha documentación… Seguiré atento por si publicas más del tema
Enhorabuena, aunque creo que te explicas mejor con cerveza ;-)
Muy buen post, Juanma, ya estoy mucho más convencido de utilizar un servidor de CI :D.
Ahora a ver si consigo que me dejen montar uno en mi empresa, aunque sea para los proyectos que hago yo solo.
Una pregunta. Según tu opinión, para trabajar con MSBuild y para empezar a familiarizarse con el tema, de los servidores que tu conoces, ¿cuál recomendarías?
Yo uso Cruise Control .NET, pero es bastante incómodo de configurar y está un poco anticuado, aunque también es verdad que una vez que lo tienes configurado funciona bien y no da guerra.
Una buena alternativa parece TeamCity de Jetbrains, pero no te puede decir mucho sobre él, sólo que tiene buena pinta.
En el Team Foundation Server creo que también hay un servidor de integración, pero me imagino que será bastante complejo, como todo el TFS.
Hola,
Buen post. Si hay alguien interesado en obtener más información, quizás os interese leer este post que escribió José Manuel Beas:
http://blog.clinkerhq.com/hosted-continuous-integration-comparative/
Un saludo
Gracias por la aportación, muy interesante.
Yo finalmente he apostado por Jenkins. Aunque hay alguna pega con el plugin de TFS y las credenciales, lo he privado también contra un repositorio en BitBucket y va bien.
Como últimamente tengo un par de dudas:
¿Que sueles utilizar para recoger bugs?
¿Que sueles utilizar para recoger nuevos requisitos?
Por último y lo que menos entiendo es el tema de la numeración de versiones. Espero sacar algo de tiempo y entenderlo algo mejor.
Para bugs y requisitos uso YouTrack de Jetbrains. Es muy fácil de montar y, aunque puedes personalizar un montón de cosas, la configuración por defecto es bastante útil.
Eso sí, no lo tengo enlazado con nada. Se pueden montar cosas más automáticas para que, por ejemplo, cuando haces un commit al control de versiones, si pones un texto determinado, te cierre la tarea en el YouTrack, pero no uso nada de eso, entre otras cosas porque mi configuración es un poco especial (una misma tarea puede referirse a varias aplicaciones distintas en repositorios o ramas distintas) y no es fácilmente automatizable.
Hola Juanma
Me interesa bastante el tema de integración continua, en mi trabajo hacemos todo esto de forma manual y me gustaría empezar a automatizarlo.
¿Que recursos me recomiendas para profundizar en el tema?
Muchas gracias.
Hola Jorge,
Mi consejo es que intentes montar algo sencillo en un entorno de pruebas para perderle el miedo y ver si te interesa dedicarle tiempo o no.
Lo primero sería montar el script de compilación (por ejemplo, con MSBuild: https://blog.koalite.com/2013/05/automatizar-la-compilacion-con-msbuild/) y luego puedes pasar a usar algún servidor de integración (seguramente el más fácil sea TeamCity, aunque no lo conozco mucho).
En cuanto a recursos como tal, me temo que no conozco ningún sitio «de referencia», pero seguro que buscando por internet encuentras infinidad de posts.
Lo bueno de empezar haciendo una prueba es que irás viendo problemas concretos y eso te guiará por donde debes ir profundizando, porque éste es un tema muy abierto y es fácil acabar perdiéndose en mil detalles.
Un saludo,
Juanma.
Ok Muchas gracias Juanma
Pingback: Integración Continua vs Continuous Delivery vs Continuous Deployment | Hello, IT.
Hola,
¿A día de hoy (2017) cómo ves empezar con la integración continua con el TFS, habrá mejorado el tema? El código lo tengo en C# y desarrollado de en VS 2017 pero las pruebas end-to-end las tengo en PyUnit, además debería lanzar unos inserts en una base de datos Oracle. ¿Dónde puedo encontrar información que me ayude a empezar con esto? He visto que para utilizar frameworks como Nunit o Jasmine hay Adaptadores, pero no he visto nada para PyUnit.
Gracias.
Hola Joan,
No conozco mucho TFS, pero me da la impresión de que es una solución que te ata demasiado a las herramientas de Microsoft.
Yo te recomendaría que montaras tu script de compilación con lo que más te guste (msbuild, poweshell, rake, da igual) y luego eso lo lances desde cualquier sitio (Ya sea un jenkins, un teamcity, etc.)
Ok, gracias por tu respuesta.
Saludos.