Diseñar para la comprobabilidad: Romper la próxima barrera
4 min leer

En nuestra serie en curso DevOps for the Win, hemos estado documentando nuestro viaje de transformación DevOps. En el capítulo 1, presentamos el Triángulo DevOps: Herramientas, arquitectura y personas en el desarrollo de productosdonde analizamos los elementos fundamentales que impulsan el éxito de DevOps. En el Capítulo 2, exploramos Los primeros pasos en nuestra transformación DevOpscompartiendo cómo sentamos las bases para cambios significativos.

Ahora, en el capítulo 3, abordamos el siguiente gran reto al que nos enfrentamos: diseñar para facilitar las pruebas. Tras mejorar la estructura de nuestro equipo, perfeccionar nuestra definición de «terminado» e implementar el análisis automatizado de código, nos dimos cuenta de que el principal cuello de botella de nuestro proceso se había desplazado al diseño de software, concretamente a la facilidad de las pruebas. El trabajo se acumulaba en la fase de pruebas, us ciclos de lanzamiento más rápidos. En este capítulo se analiza cómo superamos este reto y mejoramos nuestra capacidad para ofrecer software de alta calidad de manera eficiente.

El cuello de botella de las pruebas

La razón principal de este retraso es que las pruebas eran predominantemente manuales y se centraban en las pruebas de la interfaz de usuario y los flujos de trabajo de los usuarios. Dado que la interfaz de usuario solía ser uno de los últimos elementos en completarse, la automatización era escasa o nula y las pruebas manuales se convirtieron en el único método viable. Los casos de prueba de interfaz de usuario automatizados (por ejemplo, las pruebas de Selenium) se escribían normalmente después de completar las pruebas manuales, principalmente con fines de regresión.

Esta dependencia de las pruebas manuales

  • Retrasos significativos en las versiones de software.
  • La necesidad de múltiples implementaciones para permitir pruebas paralelas por parte de más de un miembro QA .

Esto puso de relieve la importancia de hacer el código más comprobable para optimizar el flujo de valor. Sin embargo, conseguirlo resultó más difícil de lo que habíamos previsto.

Priorización de pruebas de API y pruebas unitarias

Para afrontar este reto, nos centramos en dos áreas principales:

  1. Pruebas unitarias - Garantizar que los componentes individuales puedan probarse de forma aislada.
  2. Pruebas de API: reducción de la dependencia de las pruebas basadas en la interfaz de usuario y las bases de datos.

Pruebas API: Desplazamiento a la izquierda para una respuesta más rápida

Hemos redefinido nuestra estrategia de pruebas de API con estas mejoras clave:

  1. APIs bien definidas APIs APIs diseñaron para ser sencillas, estar bien documentadas y estar disponibles en las primeras fases del desarrollo.
  2. Evitar las bases de datos como punto de integración: depender de las bases de datos para la integración creaba dependencias que ralentizaban las pruebas. En su lugar, optamos por una integración basada en API utilizando REST sobre HTTP y GraphQL. Esto minimizó el tiempo de configuración de la base de datos y mejoró la automatización de las pruebas.

Este cambio redujo significativamente los retrasos, permitiendo una automatización más rápida y pruebas en fases tempranas, lo que demuestra que centrarse en diseños que dan prioridad a la API mejoró tanto la capacidad de prueba como la eficiencia.

Pruebas unitarias: Un cambio de mentalidad

Al principio, nuestras pruebas unitarias aumentaron rápidamente la cobertura de las pruebas, pero enseguida detectamos problemas:

  • Algunas pruebas eran superficiales, escritas sólo para cumplir los objetivos de cobertura del código.
  • Carecían de una validación significativa de la funcionalidad, los casos extremos y los escenarios de fallo.

Para contrarrestarlo, hicimos hincapié:

  • Educar a los desarrolladores en la escritura de pruebas valiosas.
  • Refactorización del código para mejorar la comprobabilidad.

Retos a la hora de escribir código comprobable

El mayor obstáculo para la eficacia de las pruebas unitarias era la falta de comprobabilidad en el propio código base. Los principales problemas eran:

  • Funciones grandes y monolíticas.
  • Componentes estrechamente acoplados.
  • Escasa separación de las preocupaciones.
  • Abstracción insuficiente.

Estos problemas dificultaban el aislamiento y las pruebas eficaces de las unidades individuales. Para solucionarlo:

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

Progresos lentos pero constantes

A pesar de estos esfuerzos, los avances fueron lentos, sobre todo en los productos heredados. La arquitectura existente limitaba el número de pruebas unitarias que podíamos escribir. Sin embargo, estas acciones no solo pretendían mejorar el código base actual, sino que eran una inversión de futuro:

  • Mejorar la capacidad de los desarrolladores para diseñar código comprobable garantizó que los futuros proyectos no sufrieran los mismos problemas.
  • Las mejoras incrementales evitaron interrupciones al tiempo que aumentaban de forma constante la cobertura de la automatización de pruebas.
  • Un cambio de mentalidad ayudó a los equipos a ver la comprobabilidad no como una carga, sino como una necesidad para el éxito sostenible de DevOps.

Para los equipos y organizaciones que se embarcan en una transformación DevOps, la comprobabilidad es un área crítica de atención. Ayuda a aliviar los cuellos de botella en la fase de desarrollo. Sin embargo, es importante gestionar las expectativas: los resultados inmediatos pueden no ser posibles, especialmente cuando se trata de grandes bases de código heredadas. Refactorizar este tipo de código sin suficientes pruebas unitarias y de regresión es un reto importante.

Lecciones de El programador pragmático

Para los casos en los que no es posible empezar con una nueva base de código, The Pragmatic Programmer: Your Journey to Mastery, de Andy Hunt y David Thomas, ofrece una guía práctica:

  • Cambios incrementales: Céntrese en refactorizar partes pequeñas y manejables del código base.
  • Desacoplar componentes: Reduzca las dependencias para que las unidades individuales sean más fáciles de probar.
  • Descomponga las funciones monolíticas: Divida las grandes funciones en unidades más pequeñas y específicas.
  • Adopte un diseño modular: Haz que el código sea más comprobable y mantenible mejorando la modularidad.

El mensaje aquí es claro: una vez resueltos los cuellos de botella iniciales, la comprobabilidad debe convertirse en un área de interés clave. Esto aliviará las limitaciones en la fase de pruebas y permitirá una entrega más rápida de software de alta calidad.

De cara al futuro

Nuestro camino hacia la mejora de la capacidad de prueba us enseñó us lecciones sobre el papel del diseño de software a la hora de facilitar transformaciones DevOps fluidas. Al centrarnos en la capacidad de prueba, abordamos los cuellos de botella, mejoramos las habilidades de los desarrolladores y sentamos las bases para el éxito futuro. Aunque los resultados inmediatos pueden ser lentos, los beneficios a largo plazo en términos de calidad, eficiencia y crecimiento del equipo hacen que esta inversión merezca la pena.

A medida que continuamos nuestro viaje DevOps, la medición del éxito se convierte en el siguiente reto clave. En el Capítulo 4, exploraremos cómo establecimos KPI y cuadros de mando para realizar un seguimiento de nuestro progreso e identificar nuevas áreas de mejora.

¡No te pierdas cómo la toma de decisiones basada en datos us ayudó us nuestros procesos DevOps y optimizar el rendimiento!