sábado, octubre 31, 2009

¡Hay que respetar la build!

sábado, octubre 31, 2009 por Martín

En una de mis últimas empresas, allá por Irlanda (como pasa el tiempo, oiga) teníamos unos 30 desarrolladores. De entre todos estos, unos 10 estaban en Polonia, los demás estábamos en Irlanda. A parte de todo esto teníamos una consultora en el Reino Unido con unas 10 personas que se dedicaban a mantenimiento. Todas estas personas podían tocar la build.

El principal, con mayúsculas, problema con el que nos encontrábamos era la cantidad de commits que se hacían. El proyecto estaba en una fase fuerte de crecimiento así que había montones de funcionalidades que subir. Esto hacía que los commits fuesen constantes, y difíciles de controlar. Y como en toda época de rush, como no, las revisiones de código destacaban por su ausencia.

Total, que con tal cantidad de commits simultáneos, por muchos tests que hiciésemos (que se hacían) era muy habitual el romper la build. Pero ese no era el problema. El problema de verdad es que la gente seguía haciendo commits cuando la build estaba rota, lo cual presenta varios problemas:

  1. No sabes que tu código va a funcionar. Por mucho que esté funcionando en tu build local, al estar la build rota, no tienes la certeza de los cambios que ha subido otra gente, así que nunca sabes si esos cambios te pueden afectar.
  2. Si no se protege la build, os cambios se acumulan. Todos esos cambios, son cambios no probados en un entorno de integración. Cuanto más tiempo pase, más grande es el problema. Cuantos más commits se acepten con una build rota, más problemas potenciales están entrando en el código. El número de errores crece. El código se vuelve más inestable.
  3. A mayor número de cambios, más difícil es el recuperar la build. Si los cambios se van acumulando y acumulando, nos encontraremos que al corregir el primer error aparecen más errores. La única solución que queda es ir corrigiendo error tras error hasta que la build vuelva a funcionar. Cuanto más código incontrolado permitamos, más tiempo nos llevará arreglar la build.
  4. En el peor de los casos, no hay otra solución que volver atrás. En el peor de los casos nos encontraremos con que el código que se ha subido se cruza y no queda más remedio que revertir los cambios, con la consiguiente perdida de tiempo. Los equipos tendrán que pararse y ver por qué sus funcionalidades se han solapado.


Todo esto se ilustra en la captura de pantalla siguiente:



Entre 1 y 2 hay un montón de commits. Llega un momento en el que alguien se da cuenta de que se ha roto la build, y comienza el proceso de arreglar la build. En 2 se arregla algo, pero no es suficiente. En 3 algo más, pero sigue sin ser suficiente. En 4 finalmente se arregla, pero han sido necesarios 10 arreglillos para conseguirlo. Cuando si la build se hubiese arreglado en el momento de detectar el error no se hubiese perdido tanto tiempo.

¿Soluciones?

  • Involucración del equipo. La build debe de ser colectiva y todo el mundo tiene parte de responsabilidad en controlar que esté siempre sana. Muy a menudo la responsabilidad recae sobre el jefe de equipo, y si este falta se hace la vista gorda. Esto al final perjudica a todos. Si alguien ve que la build está rota, debe alertar de ello e investigar a ver qué está pasando.
  • No hacer commits si la build está rota. Tan simple como esto. Si la build se rompe, lo mejor es no hacer commits. No importa que el código sea muy urgente. Como desarrolladores, si hacemos commits, puede ser mucho peor para nosotros. Es mucho más efectivo el esperar a que la build esté estable y subir el código con la seguridad de que no vamos a romper nada. Además, así nadie nos meterá en el lio de arreglar la build después ;)
  • La técnica de la botella. Si las dos primeras no funcionan, la técnica de la botella es super-efectiva. Ya había escrito sobre eso aquí. Se compra una botella, que sea grande que hará falta. Cada vez que alguien rompe la build, se ponen 50 céntimos, un euro o algo así. Que no sean 10 céntimos porque entonces no duele demasiado :) Esto es mano de santo. En cuanto alguien ve que se va dejando un par de euros al día, pasa a ser mucho más cuidadoso con los cambios que hace. Además tras unos meses se puede hacer una cena a la salud de la build. La desventaja de este sistema es que no funciona demasiado bien con equipos deslocalizados :)
  • No permitir commits. Podríamos bloquear el repositorio de código fuente si la build se rompe. Esta es una solución bastante drástica que normalmente es perjudicial. Puede funcionar bien sólo cuando el equipo es pequeño. Si el equipo es grande y hay mucho código, esta medida tendrá el efecto de cuello de botella.
  • Revertir automáticamente el código que ha causado el error. Esta fue la medida que se aplicó en la empresa que os comentaba antes. La situación era tan complicada que no quedó más remedio que revertir de inmediato cualquier código que rompiese la build. Es un poco drástico pero dada la magnitud del problema, fue necesario. La gente comenzó a ser mucho más cuidadosa cuando veía que sus cambios se rechazaban una y otra vez.
  • Build en dos fases. En sistemas y equipos complejos. Otra alternativa es mantener dos builds. Una más inestable, en la que los desarrolladores van haciendo sus commits, y otra estable que nunca fallará. La primera sería una build permisiva, pero que como siempre, habría que respetar. La segunda aceptaría sólo código que ha pasado la primera build, y siempre estaría completamente estable. La idea es que si alguien necesita el código más reciente en cualquier momento, no tenga que esperar a que se arregle la build. Esto yo lo veo como un modelo complejo sólo apto para empresas grandes que tengan necesidades muy específicas.


En fin este tema daría para mucho hablar. Seguro que muchos estais familiarizados con el mismo y lo vivis en el día a día. Me encantaría leer vuestros comentarios y saber si tenéis este tipo de problemas y que hacéis vosotros para solucionarlos.

comments

5 Respuestas a "¡Hay que respetar la build!"
Ibon Urrutia dijo...
17:44

Tengo otra solución que llevo aplicando un tiempo, y como ahora parece que las metodologías japonesas se están poniendo de moda en el mundillo técnico, tiene un nombre molón: Maneki Neko

Después del undécimo "commit-and-run" de alguno de los programadores, me fuí a la tienda china más cercana y compré un gato de los que mueven el brazo. Al día siguiente de un commit-and-run, el culpable lo tiene sobre su mesa todo el día, señalándole con el brazo: has sido tú, has sido tú, has sido tú...

Lo mejor de todo es que la gente se ha animado (en la oficina de Madrid ya han comprado un gato :-D) e incluso los programadores lo quieren incluir como castigo a otras ofensas al build: tests que no corren, commit a un build roto...etc.

Y te aseguro que cumple varios objetivos: poner de manifiesto la importancia de la integración contínua, extender la noción de que nadie es infalible (yo ya he sufrido el jodido gato ;-)) y echarte unas risas que pa qué. ;-)

Salu2


Joserra dijo...
23:18

Nosotros hemos empezado ahora a usar el truco de la botella, 50 centimos. Y parece que funciona :)
Técnicamente, ¿sabes cómo se puede hacer para que SVN bloquee los commits si hudson detecta que el buils se ha roto? Mmm, merece una busqueda en Google...
Ibon, lo del gato me ha gustado mucho :D conociendo a mi equipo seguro que les mola :) :)

Martin, una pregunta sobre los tests que haciais, ¿eran funcionales, de aceptación también? ¿qué tipo?


Martín dijo...
8:38

Muy bueno lo del gato sí.

Joserra, respecto al bloquear SVN, no estoy seguro de que se pueda por defecto. Si te digo la verdad, estaba pensando en un proceso manual. Para automatizarlo, se me ocurre que James, el servidor de correo, quizás te ofrezca callbacks para invocar comandos después de haber recibido algún correo. Eso serviría pero es un poco de curro.

Respecto a los tests, sí, eran funcionales también. El problema era que los funcionales llevaban bastante tiempo. Así que teníamos dos builds, una "con" y otra "sin", algo de lo que yo no era muy partidario porque era muy común el no romper la "sin" y el romper después la "con".

En estos casos yo creo que es mejor pasar unos días optimizando la build con tests funcionales para hacer todo más rápido que tener dos builds.


Ibon Urrutia dijo...
11:25

Lo de bloquear el SVN: tenemos los hooks, scripts que pueden correr pre-commit o post-commit.
Ya he leído de gente que ha instalado scripts que lanzan los tests pre-commit y no lo permiten si fallan (aunque siempre me ha parecido pelín exagerado).
No creo que fuera muy dificil crear un hook pre-commit que leyera el rss de builds de Hudson y no permitiera commits si el build ha fallado, pero a mi me gustan más los extreme feedback devices (estoy por pedir un Tux en mi empresa) y confiar en la responsabilidad de los programadores que bloquear los commits.
Salu2


Joserra dijo...
17:16

Descubrí que el conejo de Nabaztag tiene un plugin de Hudson :D pero me parecio un gasto demasiado elevado para pasar a la empresa. Aunque tiene que ser divertidísimo enchufarlo al build :)