Cómo obtener la integración continua correcta

El secreto para entregar a toda velocidad no se trata de sus herramientas

Por Nathaniel Tetteh en Unsplash. Así es como se logra la integración continua.

Estoy en el negocio de desarrollo de software desde hace muchos años. Vivir en Berlín también me ayuda a mantenerme en contacto con una gran comunidad de startups.

Me gusta hablar con personas de diferentes compañías para ver cómo se ven sus procesos y cuál es su cultura con respecto a los equipos, las prácticas de codificación y los estándares de calidad.

Cuando se trata de discutir sobre la Integración Continua, hay un patrón que noto la mayoría de las veces. No es raro que los desarrolladores hablen de ello como si solo se tratara de Jenkins, Travis o cualquier herramienta que utilicen.

Aparentemente, en muchos entornos, la integración continua simplemente significa tener una herramienta que verifica su último compromiso y se vuelve rojo o verde como un árbol de Navidad, dependiendo del resultado de las pruebas.

No estoy de acuerdo

Existen prácticas y procesos fundamentales que vienen antes de cualquier herramienta que pueda utilizar y que constituyen la base de la integración continua.

Sin esos, CI simplemente no es posible.

En esta publicación enumero algunos de ellos. Creo que son un buen punto de partida para lograr una verdadera integración continua.

Si está en un equipo que tiene dificultades para cumplir, o no puede coordinar sus esfuerzos de codificación con los de los compañeros de su equipo, entonces esta publicación es para usted.

No hay ramas de características

Cuando los equipos se enfrentan a la tarea de desarrollar una nueva característica, un error común que cometen es abrir una rama de características en su repositorio y comenzar a lanzar muchas confirmaciones dentro de ella.

La idea es aislarse de las restricciones del proceso de desarrollo de todo el proyecto hasta que la característica esté terminada y pueda fusionarse nuevamente en el maestro.

Representación del flujo de trabajo git de la rama de características

La idea detrás de esta práctica es más o menos "si trabajamos directamente en master, y master está conectado al ciclo de lanzamiento, lanzaremos una función de medio respaldo o algo que todavía no funciona. Peor aún, podríamos romper algo porque no hemos probado completamente nuestro código ".

Si bien esta práctica puede parecer conservadora y orientada a la seguridad, es totalmente lo contrario.

La mayoría de las veces, terminas con una versión rota justo después de fusionar tu rama de características. Sus únicas opciones en este punto son a) revertir la confirmación ob) corregir el lanzamiento.

Si no tienes suerte, ambos pueden ser caros y propensos a errores.

Si su proceso de lanzamiento no está suficientemente automatizado, revertir una confirmación tan grande puede costarle una reversión manual de la estructura de su base de datos, por ejemplo. ¿Y qué harás si alteras destructivamente tus datos?

La fijación en caliente tampoco es menos arriesgada. Para minimizar la interrupción de la experiencia del usuario, debe lanzar un parche lo antes posible. Y todos sabemos que la presión del tiempo no se lleva bien con un desarrollo bien pensado. El riesgo es agregar errores además de errores y empeorar la situación más allá de un límite aceptable.

¿Por qué las ramas de entidades no funcionan?

La razón por la cual las ramas de características no funcionan es simple: no recibe comentarios continuos durante el desarrollo.

Cuando usted y su equipo no le dan a sus partes interesadas la posibilidad de validar continuamente su trabajo en progreso, pierden el contacto con la realidad.

No hay forma de validar su progreso de desarrollo que no sea permitir que su usuario se registre continuamente.

Hay una gran falacia por la que muchos desarrolladores caen en este caso. Es la creencia de que si prueban su código lo suficiente, ya sea escribiendo pruebas o haciendo clic manualmente, pueden estar seguros de que lo que acaban de desarrollar simplemente funcionará ™.

Esto es un error y debe evitarse a toda costa.

En la medida en que usted, como desarrollador, pueda intentar pensar como su (s) usuario (s), simplemente no lo es. Los patrones de uso se pasarán por alto y no se implementarán. Los comportamientos no serán previstos, abriendo la puerta a los errores furtivos. No se descubrirán casos comerciales o procesos profundos.

En otras palabras, su software no implementará cuál es la realidad, en el mejor de los casos. O simplemente se romperá y no funcionará, en el peor de los casos. En ambos casos, se verá mal a los ojos del usuario.

Si bien el modelo Feature Branch puede sonar conservador y orientado a la seguridad, es totalmente lo contrario.

Usar funciones alterna

El primer paso fácil para lograr un buen nivel de CI es utilizar las funciones de alternancia.

Una función de alternancia es un indicador de configuración en su aplicación que puede activar y desactivar una función en particular.

Esta técnica es increíblemente fácil de implementar. En su base de código, se ve más o menos así.

Las ventajas de utilizar una función de alternancia son las siguientes:

  • puede desactivar una función en producción sin tener que volver a lanzarla. Esto es útil cuando se informa un error crítico y desea evitar que los usuarios accedan a la función dañada;
  • puede habilitar la función en un entorno canario y dejarla en producción. Esto permite a los usuarios de la etapa inicial probar la función y al equipo de desarrollo recopilar comentarios útiles antes de un lanzamiento oficial;
  • puede habilitar la función en un entorno cerrado y permitir, por ejemplo, que un gerente de producto verifique el estado del arte incluso si la función aún no está completa.

Hay una ventaja más que se desbloquea cuando comienzas a usar las funciones de alternancia.

Dado que su función se puede desactivar en producción hasta que esté completa y estable, ahora puede comenzar a fusionar confirmaciones directamente en su rama maestra.

Pequeñas confirmaciones, directamente para dominar

Hay un par de razones válidas para fusionar sus confirmaciones directamente en master siguiendo este modelo.

El modelo de fusión

¿Quien hizo esto?

La primera razón es que obtienes un historial más limpio de tu código. Sabes exactamente quién hizo qué. Esto ayuda mucho cuando otros desarrolladores necesitan aclaraciones sobre una parte específica del código.

Con las ramas de características, todas las confirmaciones se agrupan en una sola antes de fusionarse, por lo que la información se pierde y solo un desarrollador tendrá toda la culpa (git).

Fusiones fáciles, menos conflictos, reversiones más seguras

Cuanto más grande sea una rama de características, mayor será la posibilidad de que tenga conflictos de fusión.

Los conflictos de fusión pueden ser muy dolorosos ya que resolverlos es un proceso puramente manual y extremadamente propenso a errores.

Además de eso, cuando se enfrenta a un conflicto de fusión, probablemente tenga que modificar un poco la lógica de su aplicación. Esto generalmente se realiza sobre la marcha mientras se rebase. Te dejo imaginar cuál podría ser el resultado cualitativo de la codificación mientras tu cerebro está en medio de otra tarea.

Muchas veces vi desarrolladores que decían "No sé qué pasó. Resolví algunos conflictos y me equivoqué durante el rebase ”. Tal vez git tiene algunos errores, pero dudo mucho que pueda estropear durante un rebase.

Las confirmaciones más pequeñas le permiten estar en el lado seguro y evitar rebases complicados. Los conflictos son mínimos y triviales porque la extensión superficial del cambio también lo es.

Los compromisos más pequeños también son más fáciles de revertir debido a un factor psicológico. Al trabajar con grandes ramas de características, su sesgo de costo hundido lucha contra su juicio racional. Eres reacio o incluso temes revertir tal cantidad de código.

“Es mejor intentar arreglarlo. La solución será mínima en comparación con la cantidad de código. No hay necesidad de revertir todo después de todo el trabajo que hicimos ". ¿Estas palabras te suenan familiares?

Eventualmente, los pequeños commits deberían ser lo suficientemente pequeños como para que un miembro del equipo combine algo en master al menos una vez al día.

Optimizar para desarrollo paralelo

Aquí es donde el diseño de software realmente se encuentra con la integración continua.

Incluso trabajando en ramas de características aisladas, los miembros del equipo pueden bloquearse entre sí si no dividen el desarrollo de una característica con suficiente cuidado.

Es irónico que diseñamos nuestro software para que esté desacoplado y que nuestros módulos sean independientes entre sí, mientras que la ingeniería de nuestro proceso de desarrollo deja mucho que desear.

Así es como se ve una distribución de tiempo típica de las tareas de desarrollo para una sola característica.

Guybrush siempre necesita esperar a que Elaine termine su tarea; de lo contrario, no puede comenzar a trabajar en la suya. Stan está atrapado hasta que LeChuck y Elaine entregan sus partes y LeChuck finalmente se queda con una sola tarea.

Desafortunadamente, no hay una bala de plata para abordar el desarrollo de una función para que cada miembro del equipo pueda trabajar en alguna tarea en paralelo.

Lo que definitivamente ayuda aquí es mirar la característica desde el punto de vista del diseño de software e intentar comprender dónde están las líneas de corte. Básicamente estoy hablando de buscar interfaces antes de comenzar a desarrollar áreas comunes.

Una heurística útil, por ejemplo, es que si dos desarrolladores necesitan trabajar en dos áreas adyacentes, digamos un componente frontend y su contraparte de back-end, pueden acordar por adelantado en la interfaz que van a usar para poner las dos partes en comunicación.

En cambio, otro equipo podría aplicar “quien llega primero decide la regla de la interfaz”, donde dos o más desarrolladores comienzan a trabajar de manera independiente y el primero que llega a la definición de un punto de contacto lo especificará.

Las cosas aún se pueden cambiar cuando otros llegan allí y ven que la definición no se ajusta perfectamente a su caso de uso. Pero al menos nadie está bloqueado mientras tanto y la necesidad de mejorar la interfaz probablemente provocará una conversación saludable entre los miembros del equipo, así como ideas interesantes en el diseño de la función.

Conclusión

La integración continua no se trata de su herramienta.

La integración continua se trata de cómo diseñar su proceso de desarrollo.

Se necesita tiempo y esfuerzo para que funcione sin problemas.

Esto implica comprender y aplicar diferentes estrategias de sincronización no solo entre los miembros del equipo, sino también entre todas las personas involucradas en el desarrollo de una función.

Hacer que se compruebe su confirmación contra una tubería de automatización de prueba es simplemente la guinda del pastel.