En el segundo capítulo de esta serie – DevOps para el éxito, exploramos los pasos iniciales cruciales de un viaje de transformación DevOps. Los primeros pasos en cualquier viaje de transformación suelen ser los más difíciles. En DevOps, identificar los pasos iniciales correctos —aquellos que son a la vez impactantes y alcanzables— es clave para impulsar un progreso significativo.
Muchas organizaciones cometen el error de implementar ciegamente las mejores prácticas sin considerar sus limitaciones únicas, lo que a menudo lleva a la frustración y al agotamiento por el fracaso. En su lugar, se requiere un enfoque más estratégico, uno que identifique los cuellos de botella y los elimine sistemáticamente.
¿Cómo identificar los primeros pasos?
Un principio poderoso para identificar por dónde empezar procede 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, 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 se iniciaban pero no se completaban, y la cartera de pedidos seguía creciendo.
Para descubrir las causas raíz, organizamos sesiones de análisis en profundidad con los equipos de desarrollo, líderes técnicos y gerentes de producto. Estas discusiones revelaron varios problemas clave:
- Los equipos estaban asumiendo nuevas tareas antes de completar las existentes.
- No existían criterios claros de finalización de tareas; 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 dar por finalizado el trabajo, lo que provocaba defectos y retrabajos.
- Las reasignaciones frecuentes interrumpieron la estabilidad del equipo, causando ineficiencias.
Establecer Equipos Estables
Una de las primeras medidas correctivas fue la reestructuración de los equipos para lograr coherencia y enfoque. En lugar de tratar a los grupos de individuos como grupos de trabajo ad hoc, formamos equipos estables y duraderos con responsabilidades claramente definidas.
Configurar 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 complicado. Pasamos por varias iteraciones para encontrar las combinaciones adecuadas.
Siguiendo el principio de pensamiento de equipo primero descrito en Team Topologies de Matthew Skelton y Manuel Pais, nosotros:
- Se crearon equipos pequeños y autónomos (de 4 a 7 miembros) para fomentar una profunda experiencia en el campo y la rendición de cuentas.
- Se eliminaron las dependencias interequipos al asegurar que los individuos estuvieran completamente asignados a un solo equipo.
- Se ajustaron las estructuras de equipo, especialmente para funciones de apoyo como la infraestructura y la gestión de productos, con el fin de optimizar el flujo de trabajo.
Aprovechando herramientas para la visibilidad y la gestión
Una vez estructurados los equipos, pasamos a Azure DevOps para la gestión de la cartera de pedidos y del trabajo. Una plataforma unificada permitió:
- Mayor visibilidad del trabajo para que los equipos pudieran seguir el progreso de forma transparente.
- Definiciones estandarizadas de «terminado» para alinear las expectativas entre las funciones.
- Mejora en la gestión de tareas pendientes, reduciendo la desviación del alcance y los conflictos de priorización.
Aplicación de Métricas de Calidad Objetivas
Para abordar el problema de las medidas de calidad poco claras antes de marcar las tareas como «listas para la prueba», integramos una herramienta de análisis de código estático, que proporciona información objetiva sobre:
- Cobertura de código.
- Vulnerabilidades de seguridad
- Defectos de código.
También mejoramos nuestra definición de “Hecho” para que las comprobaciones de calidad fueran 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 el monitoreo y la limitación del Trabajo en Curso (WIP). Un WIP alto indicaba cuellos de botella, lo que nos permitía abordar proactivamente las áreas donde el trabajo se estancaba.
Este enfoque sistemático, arraigado en La Meta y Topologías de Equipo, sentó las bases para la mejora continua, reduciendo las dependencias y mejorando la rendición de cuentas.
En nuestro proceso de transformación DevOps, uno de los descubrimientos más importantes fue que, después de las mejoras iniciales, la principal limitación del flujo se trasladó al diseño del software, específicamente, a la capacidad de prueba de nuestro código. Después de abordar los cuellos de botella iniciales mejorando la configuración del equipo, definiendo una «Definición de Hecho» e implementando el análisis de código, notamos que el trabajo se acumulaba durante la fase de pruebas. El código desarrollado por los equipos scrum a menudo esperaba en la fila para ser probado por los miembros de control de calidad dentro de los mismos equipos scrum.
Abordando el Próximo Cuello de Botella: La Brecha en las Pruebas
Una vez resueltos los cuellos de botella iniciales, surgió una nueva limitación: los retrasos en las pruebas. Mientras las tareas de desarrollo progresaban eficientemente, las pruebas se convirtieron en un obstáculo, impidiendo lanzamientos más rápidos. Tras la investigación, identificamos desafíos clave que ralentizaban el proceso:
- Dependencia de las pruebas manuales: La mayoría de las pruebas se ejecutaron manualmente, lo que llevó a ciclos de retroalimentación lentos y a una detección de defectos tardía.
- Dependencia de la interfaz de usuario (UI) en las pruebas: Dado que los componentes de la interfaz de usuario (UI) solían completarse al final, las pruebas no podían comenzar hasta tarde en el ciclo de desarrollo.
- Falta de automatización proactiva: Las pruebas automatizadas, como las pruebas de interfaz de usuario basadas en Selenium, solo se escribieron después de las pruebas manuales, lo que limitó su eficacia en la validación en etapas tempranas.
Transición a enfoques de prueba primero
Para romper este cuello de botella, priorizamos las pruebas unitarias y las pruebas de API sobre las pruebas de interfaz de usuario. El cambio requirió cambios fundamentales en la mentalidad de desarrollo y el diseño de software:
Mejoras en las Pruebas de API
Para hacer que las pruebas de API sean eficientes:
- APIs bien definidas: Las APIs debían ser sencillas, estar bien documentadas y disponibles en las primeras fases de desarrollo para que los evaluadores pudieran crear casos de prueba de forma proactiva.
- Evitar la base de datos como punto de integración:
- El uso de bases de datos como punto de integración entre equipos creó dependencias que ralentizaron las pruebas.
- La creación de casos de prueba requería configurar bases de datos con información compleja, lo que aumentaba el tiempo de configuración y limitaba la capacidad de probar múltiples situaciones.
- Al pasar a la integración basada en API (principalmente REST sobre HTTP y, más recientemente, GraphQL), reducimos significativamente los retrasos y las complejidades causadas por las dependencias de la base de datos.
Este cambio permitió un progreso más rápido en la automatización de casos de prueba para APIs, demostrando que centrarse en diseños API-first mejoró tanto la capacidad de prueba como la eficiencia.
Fortalecimiento de las prácticas de pruebas unitarias
Las pruebas unitarias plantearon un desafío mayor:
- Progreso rápido, luego cobertura aparente: Inicialmente, logramos altos niveles de cobertura de código, pero las revisiones de código revelaron que muchas pruebas unitarias eran superficiales, escritas solo para cumplir con los objetivos de cobertura. Estas pruebas no lograron verificar funcionalidades significativas, cubrir escenarios de fallo o validar casos extremos.
- 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 al detectar problemas a tiempo.
Desafíos al escribir código comprobable
El mayor obstáculo para las pruebas unitarias eficaces fue la falta de capacidad de prueba en el propio código base. Los problemas clave incluyeron:
- Grandes funciones monolíticas.
- Componentes fuertemente acoplados.
- Mala separación de responsabilidades.
- Abstracción insuficiente.
Estos problemas dificultaron el aislamiento y la prueba efectiva de unidades individuales. Para abordar esto, nosotros:
- Se proporcionaron directrices.: Compartimos las mejores prácticas sobre:
- Selección de funciones para pruebas unitarias.
- Refactorización de código para mejorar la capacidad de prueba.
- Diseño y uso de simulacros para facilitar las pruebas.
- Centrado en mejoras incrementales: Se animó a los desarrolladores a realizar cambios pequeños y significativos para mejorar la capacidad de prueba con el tiempo.
Avances, desafíos y el camino a seguir
Para los productos heredados, mejorar la capacidad de prueba siguió siendo un proceso lento debido a limitaciones arquitectónicas. Sin embargo, estas acciones no solo estaban dirigidas a mejorar la base de código actual, sino que fueron una inversión en el futuro:
- Los desarrolladores adquirieron mejores hábitos, integrando la capacidad de prueba en las nuevas bases de código.
- Los equipos se volvieron más autosuficientes, lo que redujo la dependencia de los esfuerzos externos de QA.
- La organización evitó repetir errores pasados, asegurando que los productos futuros fueran más fáciles de mantener y evolucionar.
¿La conclusión clave? DevOps no se trata de implementar herramientas o listas de verificación. Se trata de mejorar continuamente el flujo de trabajo, una restricción a la vez.
En el próximo capítulo de esta serie, exploraremos “Diseño para la Testabilidad”. Profundizaremos en cómo la mejora del diseño de software puede eliminar las limitaciones de las pruebas, permitiendo ciclos de retroalimentación más rápidos y una mayor calidad del software —primero en las estructuras de equipo, luego en la gestión del flujo de trabajo y finalmente en la testabilidad— sentamos las bases para una transformación DevOps sostenible.