Mentalidad QA: la fiabilidad como concepto de diseño
Los equipos de ingenieros de Fastly son capaces de todo: concienzudos en el diseño de las arquitecturas, escriben códigos elegantes y resuelven con meticulosidad trabajos complejos y de envergadura. ¿Por qué son necesarias entonces las garantías de calidad o QA (quality assurance, por sus siglas en inglés)? Los profesionales de control de calidad realizan su trabajo con una perspectiva específica: un planteamiento que es a la vez rígido y flexible. En los departamentos de control de calidad se crean pruebas para garantizar la fiabilidad y capacidad de respuesta de nuestra plataforma, para que nuestros clientes puedan ofrecer experiencias seguras y fiables a sus usuarios. Aunque suele ser el último paso hasta el lanzamiento de un software, si el proceso de control de calidad estuviera integrado ya en las primeras fases del proyecto, la fiabilidad podría convertirse en un factor inherente al sistema, con el consiguiente ahorro de tiempo y esfuerzo que esto supondría, en lugar de tener que realizar reparaciones a posteriori. En este post, voy a explicar cómo funciona esta idea, me detendré en nuestra metodología de control de calidad en Fastly y compartiré cómo podría aplicarse a tu organización.
Metodología
El objetivo de probar un software es poder ofrecer garantías de fiabilidad a tus clientes. La fiabilidad supone que tu proyecto cumpla lo prometido, a la vez que aplica una lógica razonable ante circunstancias imprevistas o difíciles. ¿Podemos garantizar que es fiable antes de que llegue a los usuarios? La fiabilidad puede integrarse ya desde el principio prestando atención a cuatro áreas de pruebas importantes.
Son cuatro factores: corrección, resiliencia ante los errores, rendimiento y solidez, y cada uno requiere un estudio minucioso de forma continuada, en lo posible, mediante un proceso automatizado. Aplico esta metodología cuando trabajo en un proyecto nuevo. Si los resultados de las pruebas de cada área de atención son satisfactorios, tengo la seguridad de que el proyecto está listo para su lanzamiento y que se comportará de forma fiable.
Con este modelo, el proceso de control de calidad crea series de pruebas para explorar por completo cada área. Pero ¿cómo escribimos los códigos de pruebas que cumplan este objetivo? Hay que abordarlo como un proceso en dos partes. Se empieza con entradas concretas que se emparejan con salidas definidas; así desarrollamos una imagen integral y claramente visible del comportamiento de la característica. Entonces, combinamos escenarios razonables e incluso ilógicos para ejecutar el software empleando toda nuestra creatividad hasta llevarlo a un punto extremo en el que dará error.
Fase 1: Conceptos claros
Imagina una solicitud de una característica nueva para una API existente:
Solicitud: poder definir una variable "user" con un nombre de usuario
Los nombres de usuario son cadenas y no deben estar vacías.
Funcionalidad deseada:
http://myexample.sample/set?user=username
Todo esto parece muy fácil de comprender y de desarrollar rápido con código. Si eres un desarrollador meticuloso, incluso podrías lanzar un par de pruebas unitarias: una para asegurarte de que al enviar un nombre de usuario este se añada correctamente, y otra para confirmar que una cadena vacía se rechace como nombre de usuario. Para la funcionalidad solicitada, deberías plantearte estas preguntas:
¿Cuál es el número mínimo de caracteres para el nombre de usuario?
¿Cuál es el número máximo de caracteres para el nombre de usuario?
¿Qué caracteres se permiten para el nombre de usuario? ¿Hay alguno que no se pueda usar?
¿Se puede añadir el mismo nombre de usuario dos veces? ¿Son obligatorias las credenciales exclusivas?
¿Un nombre de usuario pude contener Unicode?
¿Hay nombres de usuario protegidos (p. ej. "admin" o "user")?
¿Cuántas solicitudes se pueden emitir simultáneamente?
¿Cuántos nombres de usuario se pueden guardar?
¿Se muestran mensajes de error y son fáciles de entender?
Básicamente, nos interesa saber qué entradas y salidas son las deseables. La solicitud original apenas puede someterse a prueba, porque no especifica con precisión qué se entiende por comportamiento deseado en cada área de prueba. Con una metodología de control de calidad asumida, los problemas se abordan con claridad y se toman decisiones relacionadas con el comportamiento del programa en primera instancia, en lugar de considerarlas como un aspecto secundario. Con el código ya implementado, el comportamiento actual pasa a ser el predeterminado y puede ser difícil revertirlo o sustituirlo. Escribe un código que refleje lo que quieres, no lo que esperas.
Por otro lado, al crear unas especificaciones completas para tu proyecto obtienes, como efecto colateral, una lista de objetivos de prueba. Cada especificación (una entrada con salida asociada) debería generar pruebas unitarias o de integración, que garanticen salidas (o respuestas) apropiadas para cada comportamiento.
Fase 2: Jugar a ser creativo
Tener claras las especificaciones de diseño y asegurarte de que se cumplan no te aporta mucho; tienes que emplear toda tu creatividad para someter todo a prueba.
Cuando tengo que probar una característica nueva, me imagino que es como un juguete nuevo, por ejemplo, una brillante pelota roja que se puede lanzar y hacer botar una y otra vez. Es lo que se puede esperar de una pelota roja, pero eso no significa que pase con éxito una evaluación de control de calidad y que esté lista para nuestros clientes.
¿Botará por la noche o la perderé en la oscuridad? ¿Seguirá botando igual mañana, dentro de un mes o un año? ¿Me sirve combinada con mis otros juguetes, como los guantes o los bates de béisbol? ¿Cuántos botes aguantará hasta que se rompa? ¿Puedo cortarla por la mitad? Si la corto por la mitad, ¿puedo volver a pegarla? ¿Botará sobre una alfombra o el césped? ¿Es tóxica o se puede comer?
Estas situaciones no están contempladas en las especificaciones de funcionalidad básicas, pero es inteligente planteárselas, ya que representan comportamientos de los usuarios reales. Los clientes no siempre tratan las características de la forma que esperamos o deseamos, pero si estas situaciones realmente pueden darse, se darán.
Plantearse todas estas cuestiones es fundamental para determinar una serie de pruebas de integración que garanticen que el usuario final va a recibir un producto que le satisfaga. Cuanto más tiempo dediques a cuestionarte estos posibles escenarios, mejor sabrás preverlos incluso antes de empezar a desarrollar el código. En definitiva, el diseño integrará fiabilidad y capacidad de respuesta en lugar de tener que aplicar correcciones a posteriori.
Es fundamental ser creativos imaginando todo tipo de situaciones de desastre, solo así podremos probar la calidad con resultados fiables. Los desarrolladores suelen quejarse de que "maltrato" sus proyectos, pero al final, los productos que lanzamos son fiables.
Por último
Ahora que piensas como un ingeniero de control de calidad, tienes que aclarar las peculiaridades y carencias de cada funcionalidad del proyecto, y luego esforzarte en sobrecargarlo, sabotearlo o destruir con todas las armas a tu alcance el producto final. Es cierto, lleva tiempo, pero el resultado final es que el producto es fiable, con menos fallos, por lo que te ahorras el mantenimiento y las revisiones, y tus usuarios finales disfrutan de una funcionalidad garantizada.