Los primeros pasos en nuestra transformación de DevOps
5 min de lectura

En el segundo capítulo de esta serie, «DevOps para el éxito», exploramos los pasos iniciales cruciales de un proceso de transformación de DevOps. Los primeros pasos en cualquier proceso de transformación suelen ser los más difíciles. En DevOps, identificar los pasos iniciales adecuados (que sean tanto impactantes como alcanzables) es fundamental para impulsar un progreso significativo.

Muchas organizaciones cometen el error de implementar a ciegas las mejores prácticas sin tener en cuenta sus limitaciones particulares, lo que a menudo genera frustración y fatiga ante el fracaso. En su lugar, se requiere un enfoque más estratégico: uno que identifique los cuellos de botella y los elimine de forma sistemática.

¿Cómo identificar los primeros pasos?

Un principio poderoso para identificar por dónde empezar proviene de una investigación publicada hace 30 años en La meta: un proceso de mejora continua, de Eliyahu M. Goldratt. La idea es sencilla: identificar el cuello de botella principal, es decir, el factor limitante que restringe el rendimiento del sistema. En nuestro caso, esto quedó claro cuando vimos que el trabajo se acumulaba en el equipo de desarrollo. Las tareas empezaban pero no se terminaban, y el trabajo pendiente seguía creciendo.

Para identificar las causas principales, organizamos sesiones exhaustivas con los equipos de desarrollo, los líderes técnicos y los gestores de productos. Estas conversaciones sacaron a la luz varios problemas clave:

  • Los equipos asumían nuevas tareas antes de terminar las existentes.
  • No existían criterios claros para determinar cuándo se completaba una tarea; lo que los desarrolladores consideraban "terminado" a menudo difería de las expectativas de las partes interesadas.
  • No había medidas de calidad objetivas antes de marcar el trabajo como finalizado, lo que provocaba defectos y la necesidad de repetir tareas.
  • Las reasignaciones frecuentes afectaban a la estabilidad del equipo y generaban ineficiencias.

Establecimiento de equipos estables

Una de las primeras medidas correctivas consistió en reestructurar los equipos para garantizar la coherencia y la concentración. En lugar de tratar a grupos de personas como fuerzas de tarea temporales, formamos equipos estables y duraderos con responsabilidades claramente definidas.

Crear algunos equipos, como los de desarrollo, fue relativamente sencillo. Sin embargo, organizar los equipos de soporte —como los de infraestructura, gestión de proyectos, gestión de productos y análisis de negocio— resultó más complejo. Pasamos por varias iteraciones hasta encontrar las combinaciones adecuadas.

Siguiendo el principio de anteponer el equipo descrito en el libro Team Topologies, de Matthew Skelton y Manuel Pais, nosotros:

  • Creamos equipos pequeños y autónomos (de 4 a 7 miembros) para fomentar un profundo conocimiento especializado y la rendición de cuentas.
  • Eliminamos las dependencias entre equipos al garantizar que cada persona estuviera asignada por completo a un único equipo.
  • Iteramos sobre las estructuras de los equipos, especialmente para las funciones de soporte como infraestructura y gestión de productos, con el fin de optimizar el flujo de trabajo.

Uso de herramientas para la visibilidad y la gestión

Una vez estructurados los equipos, pasamos a utilizar Azure DevOps para la gestión del trabajo pendiente (backlog) y de las tareas. Una plataforma unificada permitió:

  • Una mejor visibilidad del trabajo para que los equipos pudieran seguir el progreso de forma transparente.
  • Definiciones estandarizadas de lo que significa «terminado» para alinear las expectativas entre las distintas funciones.
  • Una gestión mejorada del trabajo pendiente, lo que redujo la expansión descontrolada del alcance y los conflictos de priorización.

Aplicación de métricas de calidad objetivas

Para solucionar el problema de la falta de medidas de calidad claras antes de marcar las tareas como «listas para pruebas», integramos una herramienta de análisis estático de código, que ofrece información objetiva sobre:

  • Cobertura de código
  • Vulnerabilidades de seguridad
  • Malos hábitos de programación (code smells)

También mejoramos nuestra definición de «Terminado» para que las revisiones de calidad sean un paso obligatorio antes de marcar los elementos de trabajo como completados.

Gestión del trabajo en curso (WIP)

Uno de los cambios más impactantes fue supervisar y limitar el trabajo en curso (WIP). Un nivel alto de WIP indicaba cuellos de botella, lo que nos permitió abordar de forma proactiva las áreas en las que el trabajo se estaba estancando.

Este enfoque sistemático, basado en The Goal y Team Topologies, sentó las bases para la mejora continua, reduciendo las dependencias y mejorando la rendición de cuentas.

En nuestro trayecto de transformación hacia DevOps, una de las conclusiones más significativas fue que, tras las mejoras iniciales, la principal limitación del flujo pasó a ser el diseño de software, específicamente la capacidad de prueba de nuestro código. Tras resolver los primeros cuellos de botella mediante la mejora de la estructura de los equipos, la definición de una «definición de hecho» y la implementación del análisis de código, observamos que el trabajo se acumulaba durante la fase de pruebas. El código desarrollado por los equipos de scrum a menudo esperaba en cola para ser probado por los miembros de QA dentro de los mismos equipos de scrum.

Abordaje del siguiente cuello de botella: La brecha de pruebas

Una vez superados los cuellos de botella iniciales, surgió una nueva limitación: los retrasos en las pruebas. Aunque las tareas de desarrollo avanzaban con eficacia, las pruebas se convirtieron en un obstáculo que impedía lanzamientos más rápidos. Tras realizar un análisis, identificamos los principales problemas que ralentizaban el proceso:

  • Dependencia de las pruebas manuales: La mayoría de las pruebas se realizaban de forma manual, lo que ralentizaba los ciclos de retroalimentación y retrasaba la detección de errores.
  • Dependencia de la interfaz de usuario en las pruebas: Como los componentes de la interfaz de usuario solían ser los últimos en completarse, las pruebas no podían comenzar hasta una fase avanzada del ciclo de desarrollo.
  • Falta de automatización proactiva: Las pruebas automatizadas, como las pruebas de interfaz de usuario basadas en Selenium, solo se redactaban después de las pruebas manuales, lo que limitaba su eficacia en la validación de fases tempranas.

Transición hacia enfoques orientados a las pruebas

Para superar este cuello de botella, priorizamos las pruebas unitarias y de API por encima de las de la interfaz de usuario. Este cambio requirió modificaciones fundamentales en la mentalidad de desarrollo y en el diseño del software:

Mejoras en las pruebas de API

Para lograr que las pruebas de API fueran eficientes:

  1. APIs bien definidas: Las APIs debían ser sencillas, estar bien documentadas y estar disponibles en las primeras fases del desarrollo para que los evaluadores pudieran crear casos de prueba de manera proactiva.
  2. Evitar el uso de la base de datos como punto de integración:
    • El uso de bases de datos como punto de integración entre equipos generaba dependencias que ralentizaban las pruebas.
    • La creación de casos de prueba requería configurar bases de datos con datos complejos, lo que aumentaba el tiempo de configuración y limitaba la capacidad de probar múltiples escenarios.
    • Al migrar a la integración basada en API (principalmente REST sobre HTTP y, más recientemente, GraphQL), redujimos significativamente los retrasos y la complejidad causados por las dependencias de las bases de datos.

Este cambio permitió avanzar más rápidamente en la automatización de casos de prueba para APIs, lo que demuestra que centrarse en diseños que priorizan las APIs mejoró tanto la capacidad de prueba como la eficiencia.

Refuerzo de las prácticas de pruebas unitarias

Las pruebas unitarias plantearon un reto mayor:

  • Progreso rápido y, después, fachada de cobertura: Al principio, alcanzamos niveles elevados de cobertura de código, pero las revisiones de código revelaron que muchas pruebas unitarias eran superficiales y se habían escrito únicamente para cumplir con los objetivos de cobertura. Estas pruebas no evaluaban funcionalidades importantes, no abarcaban escenarios de fallo ni validaban casos límite.
  • Cambio de mentalidad: Los desarrolladores necesitaban comprender el valor de las pruebas unitarias, no solo para mejorar la calidad del código, sino también para acelerar el desarrollo mediante la detección temprana de problemas.

Retos en la redacción de código comprobable

El mayor obstáculo para realizar pruebas unitarias eficaces fue la falta de capacidad de prueba en el propio código. Entre los problemas principales se encontraban:

  • Funciones grandes y monolíticas.
  • Componentes fuertemente acoplados.
  • Deficiente separación de intereses.
  • Abstracción insuficiente.

Estos problemas dificultaban el aislamiento y la evaluación eficaz de las unidades individuales. Para solucionarlo, llevamos a cabo lo siguiente:

  1. Directrices proporcionadas: Compartimos las mejores prácticas sobre:
    • Selección de funciones para las pruebas unitarias.
    • Refactorización del código para mejorar la capacidad de prueba.
    • Diseño y uso de simulaciones para facilitar las pruebas.
  2. Enfoque en mejoras incrementales: Se alentó a los desarrolladores a realizar cambios pequeños y significativos para mejorar la capacidad de prueba con el tiempo.

Progreso, desafíos y el camino por delante

En el caso de los productos heredados, mejorar la capacidad de prueba siguió siendo un proceso lento debido a las limitaciones arquitectónicas. Sin embargo, estas acciones no solo tenían como objetivo mejorar el código base actual, sino que eran una inversión en el futuro:

  • Los desarrolladores desarrollaron mejores hábitos e integraron la capacidad de prueba en los nuevos códigos base.
  • Los equipos se volvieron más autónomos, lo que redujo la dependencia de los esfuerzos externos de QA.
  • La organización evitó repetir errores pasados, lo que garantizó que los futuros productos fueran más fáciles de mantener y evolucionar.

¿La conclusión principal? DevOps no consiste en implementar herramientas o listas de comprobación. Se trata de mejorar continuamente el flujo de trabajo, una limitación a la vez.

En el próximo capítulo de esta serie, exploraremos «Diseño para la capacidad de prueba». Profundizaremos en cómo la mejora del diseño de software puede eliminar las restricciones de las pruebas, lo que permite ciclos de retroalimentación más rápidos y una mayor calidad del software (primero en las estructuras de equipos, luego en la gestión de flujos de trabajo y, por último, en la capacidad de prueba), estableciendo así las bases para una transformación sostenible de DevOps.

Suscribirse al blog de Freyr

Política de Privacidad