martes, 17 de febrero de 2009

¿Que son los Casos de Uso?

La metodología requiere que es incluyan “Casos de Uso” en el Análisis de un sistema, pero… ¿tenemos claro realmente qué son los casos de uso? ¿Por qué se hacen? ¿Para que sirven?

Una historia ficticia (no tanto)

Empezaré contando una historia ficticia: Julián fue a encargarse un coche a medida. Puso varios requisitos: que fuera blanco, con techo solar, que no fuera muy contaminante y que tuviera lector de MP3 para escuchar música. Cuando tres meses más tarde fue a buscar su “coche”, le entregaron en el taller esto:


Julián no estaba muy convencido, pero los del taller le insistieron de que era lo mejor y que además cumplía con todos los requisitos que habían acordado y que él mismo había firmado de su puño y letra. Al final, como necesitaba el coche urgentemente se lo llevó… No llegó muy lejos porque el gas propano del pequeño depósito se acabó enseguida. Julián no estaba nada contento… “Yo quería este coche para irme en verano a la playa y para ir a trabajar todos los días y ¡ni siquiera puedo ir al Carrefour a hacer la compra! ¡no tiene maletero!” Los del taller le respondieron: “acordamos unos requisitos y este coche cumple con todos, no dijimos nada de llevar maletas o la compra, si no puedes usar el coche para ir al Carrefour lo siento mucho, tenías que habérmelo dicho desde el principio”

 

Requisitos

Casos de Uso

El coche será de color blanco

Usuario - Ir a trabajar todos los días

El coche tendrá un techo solar

Usuario - Ir a la playa en verano

El coche no consumirá combustible diesel

Usuario - Ir al carrefour a hacer la compra

El coche tendrá lector de MP3 que permitirá escuchar música

 

 

Los casos de uso se inventaron para evitar la situación en la que,
con la mejor intención, se recoge una lista de requisitos detallados con todo cuidado, 
se negocian y acuerdan con el usuario durante largo tiempo, 
se diseña, construye y prueba el software respetando escrupulosamente esos requisitos
y al final, el software resultante NO SIRVE PARA NADA
y además, no sabemos cómo hemos llegado a esta situación.


Si, vale, pero… ¿Qué son los casos de uso?

La definición académica dice que: “Es la descripción de una secuencia de eventos que, tomados en conjunto hacen que un sistema haga algo de utilidad para un usuario”. En UML se representan así:

En lenguaje corriente, un caso de uso responde a la pregunta: “¿Para qué necesita el usuario el sistema?” “¿Qué quiere hacer con el sistema?”. Los usuarios de Microsoft Word no quieren cambiar la fuente del texto, ni cortar y pegar palabras, ni corregir textos ortográficamente. Estas acciones, aunque necesarias, no son útiles en si mismas. Los usuarios, lo que realmente quieren es escribir cartas, redactar informes o preparar documentos técnicos, para declarar su amor, conseguir presupuesto para su departamento o vender un producto. Y la mayoría de usuarios si pudieran hacerlo sin utilizar un ordenador, lo harían.

Entonces … ¿Cómo se utilizan?

Vale… ya sabemos lo que es un caso de uso… pero ¿cómo se utilizan? ¿cómo identifico los casos de uso de un sistema y cómo me ayudan a evitar el desastre de trabajar durante meses y que al final no sirva para nada?

En primer lugar, para identificarlos hay que hablar con los usuarios en su propio lenguaje y no forzar al usuario a que haga nuestro trabajo de determinar los requisitos que mejor se ajustan a sus necesidades. No le preguntes cuantos asientos quiere en su coche, sino cuantos hijos tiene y si piensa aumentar la familia o llevar a la abuela. No le preguntes el tamaño del coche, sino si tiene plaza de garaje o si va a hacer viajes largos o cortos. El desarrollador es el experto en sistemas de información, y debe tomar la responsabilidad de hacer un buen sistema. Hay que construir el sistema que el usuario “necesita”, no el que el usuario “quiere” o “cree que quiere”. De todo lo que nos diga el usuario debemos filtrar las necesidades reales y no cerrar requisitos anticipadamente. Si el usuario dice: “necesito una BB.DD. Oracle”, probablemente tenga razón y realmente la necesite, pero debe ser el desarrollador el que decida si las necesidades, los casos de uso justifican realmente una BBDD y si Oracle es el mejor fabricante.

Una vez que tenemos claros los casos de uso del sistema, y los tenemos priorizados identificaremos los requisitos que el sistema debe cumplir para que los casos de uso se puedan completar. Habrá requisitos explícitos “quiero el coche blanco” e implícitos (de los que nadie se acuerda inicialmente pero que son esenciales para que el sistema sirva de algo), Por ejemplo: para el caso de uso “ir a la compra con el coche”, tengo que poner varios requisitos: “queremos un coche”, “que pueda circular por las calles de la localidad donde vive el usuario”, “que pueda cargar la compra cómodamente”, “que sea fácil de aparcar”, “que tenga una autonomía suficiente para al menos llegar desde mi casa hasta el hipermercado”. Si consideramos el caso de uso “ir a la playa en verano”, muchos requisitos serán repetidos “queremos un coche”, otros serán parecidos “que pueda cargar el equipaje cómodamente” y otros serán totalmente nuevos “quiero aire acondicionado”.

Ya tengo los Casos de Uso y los requisitos ¿por donde empezamos a desarrollar?

Los casos de uso son una buena forma de “ordenar” el trabajo de desarrollo. En lugar de empezar por el primer requisito y terminar por el último, debemos empezar por el caso de uso más importante o prioritario, implementando los requisitos necesarios para completar este caso de uso.

Durante el desarrollo, se toman a diario muchas pequeñas (o grandes) decisiones que no están especificadas en los requisitos. (p.ej. “¿Qué tamaño debe tener el depósito de combustible?”). Si al tomar cada pequeña decisión no tenemos presentes los casos de uso, corremos el riesgo de hacer que el sistema no sea utilizable (no se puedan completar los casos de uso) aun a pesar de cumplir todos los requisitos que habíamos identificado explícitamente.

¿Y si hay cambios?

Cuando alguien (el usuario o un desarrollador) proponga cambios en los requisitos, tenemos que contrastarlos con los casos de uso, para ver si el cambio se justifica con la utilidad que se le piensa dar al sistema o es solamente una ocurrencia. A veces al examinar requisitos a la luz de los casos de uso vemos que son innecesarios o menos prioritarios de lo que parecía (por ejemplo Gmail no tiene carpetas para organizar el correo… el usuario no quiere organizar su correo en categorías, lo que realmente quiere es encontrar los correos antiguos cuando los necesita)

Y también las pruebas

Otra utilidad importante de los Casos de Uso es que ayudan a organizar y planificar las pruebas. El equipo de pruebas puede seguir la secuencia de eventos documentada en el caso de uso y verificar si el sistema, efectivamente, sirve para lo que hemos dicho que sirve o hay algo que lo impida. Esta forma de probar es mucho más útil que probar requisito a requisito. Como ya hemos dicho, podemos tener un sistema con el 90% de los requisitos implementados de acuerdo a la especificación y que la utilidad del sistema sea el 0% porque no se puede completar ningún caso de uso. Lamentablemente tener completado el 100% de los requisitos no garantiza la utilidad del sistema.

En resumen

Los casos de uso ayudan a que las entrevistas entre desarrollador y usuario se lleven a cabo en el lenguaje del usuario, centrándose en la utilidad prevista del sistema, en lugar de negociar requisitos aislados que tomados por separado no tienen ningún sentido.

Además, ayudan a identificar los requisitos “ocultos” o “implícitos” que se dan por supuesto por parte del usuario y que deben ser tenidos en cuenta por parte del desarrollador.

Sirven para ordenar y priorizar las tareas de desarrollo, para implementar primero los requisitos necesarios para completar los casos de uso más importantes y prioritarios.

Finalmente son una buena forma de organizar y planificar las pruebas. Podemos evaluar el grado de avance de una aplicación por el número de casos de uso completados, es decir por la cantidad de cosas útiles para el usuario que es capaz de hacer.

Epílogo

El ejemplo del “coche” que no sirve para ir a hacer la compra puede parecer exagerado, pero en este caso se aplica lo de que la realidad siempre supera a la ficción. Yo he visto con mis propios ojos a un desarrollador explicarle al Director de Tecnología de Correos que con el flamante sistema GIS que le estaba desarrollando se podían cambiar las rutas de los carteros pero no se podía buscar la ruta que le correspondía a un cartero e imprimirla en un papel. “No estaba en los requisitos” fue su explicación.

 

martes, 27 de enero de 2009

Verificación vs Validación

  • Verificación: comprobar que una aplicación software cumple correctamente todos los requisitos
  • Validación: comprobar que una aplicación software sirve para el propósito para el que fue concebida, es decir, que además de cumplir los requisitos sirve para algo.
Reflexión: ¿Es posible que una aplicación cumpla todos los requisitos y no sirva para nada?

martes, 20 de enero de 2009

Usabilidad: Software Considerado

Buscando ejemplos de malos mensajes de error o preguntas al usuario, he encontrado este artículo que me parece muy interesante, sobre las cualidades (desde el punto de vista de usabilidad) que debe tener un Software respetuoso con el usuario.

http://www.codinghorror.com/blog/archives/000550.html

viernes, 9 de enero de 2009

Simplicidad - mi palabra preferida

Simplicity--the art of maximizing the amount 
of work not done--is essential.

lunes, 14 de abril de 2008

KISS = Keep It Simple Stupid!

Este principio (KISS) nos dice que en el desarrollo de software, siempre debemos optar por la solución más sencilla (que no por ello es la más fácil de implementar), evitando cualquier indicio de complejidad innecesaria.

El ejemplo máximo de sencillez es la interfaz de usuario de la aplicación con más éxito de la historia, Google: ¿que puede ser mas sencillo que un cuadro de texto y un botón para ejecutar la búsqueda? Otra cosa es la algoritmia y tecnología que hay detrás de estos botones para que todo funcione como se espera, pero el diseño de la aplicación es indiscutiblemente el más sencillo posible.

El principio KISS es fundamental para el diseño de interfaces de usuario, sin olvidarnos del modelo lógico que subyace debajo de los controles y ventanas.

También se aplica al diseño arquitectónico de la aplicación. El diseño tiene que tener tantas capas y componentes como sea necesario para cumplir los requisitos pero nada más. Esto es tan evidente cuando se escribe, como difícil de conseguir. A la hora de diseñar tenemos la tentación de añadir capas y componentes "por si acaso", para hacer el sistema "flexible" y "adaptable" y terminamos por tener una aplicación llena de modulos, componentes y clases que no tienen una función muy clara, y que en lugar de proporcionar la flexibilidad prometida, son un obstáculo a la hora de encontrar errores, optimizar el rendimiento o simplemente evolucionar el sistema. (está relacionado con el principio YAGNI, del que hablaré otro dia)

En un nivel más bajo, en el código, también es importante mantener las ideas claras y el código sencillo. Un código complejo, abigarrado, con métodos de cientos de líneas, condiciones crípticas dentro de los if() o while(), bucles anidados con varios niveles... son un indicador de código de mala calidad, resultado del trabajo descoordinado de varios desarrolladores en diferentes momentos de tiempo, sin una comprensión completa del propósito de dicho código. Por eso es tan importante dedicar tiempo suficiente a la refactorización periódica del código, para mantenerlo limpio, estilizado, coherente, ligero y eficiente.

Normalmente la sencillez es síntoma de que se están haciendo bien las cosas: cuando vemos una competición de esquí, muchas veces podemos distinguir al esquiador bueno por sus movimientos sencillos y sin brusquedades, lo hace "fácil" (aunque esa facilidad aparente sea fruto de miles de horas de entrenamiento). Igual pasa en otros deportes de técnica compleja como el golf, la gimnasia rítmica o incluso el fútbol.

Volviendo al software, un diseño tiene que ser tan simple como se pueda, y no más.

Sorprendentemente, hacer las cosas sencillas cuesta más trabajo que hacerlas complicadas. Normalmente un diseño o código suele ser fruto de varias tentativas y aproximaciones hasta que logramos una solución que cumple todos los requisitos. Inicialmente nos preocupamos de que sea eficaz (es decir que haga lo que tiene que hacer) y vamos añadiendo elementos hasta que lo conseguimos. El paso hacia la sencillez es posterior y deliberado: consiste en examinar el diseño y simplificar y quitar elementos innecesarios hasta que ya no podemos quitar ninguno.

Blaise Pascal se despedía en una de sus célebres cartas con una disculpa: "Si hubiera tenido más tiempo hubiera escrito la carta más corta". Lo mismo digo: ojala fuera capaz de decir lo mismo con menos palabras.

lunes, 31 de marzo de 2008

Realidades y Mentiras sobre la Ingeniería del Software

Leo en este blog una referencia a un libro que leí hace tiempo Facts and Fallacies about Software Engineering, y que creo que resume perfectamente muchos de los problemas que se ven a diario desarrollando software:

Interesante recordatorio.

miércoles, 19 de marzo de 2008

KISS, DRY, YAGNI...

La comunidad de defensores de los métodos ágiles de desarrollo es muy aficionada a los acrónimos crípticos. Debe ser una forma de reconocerse unos a otros, y de impresionar a los no iniciados en la materia... en cualquier caso, merece la pena ver lo que quieren decir, porque las ideas que hay detrás son muy interesantes en nuestra búsqueda del Software de Calidad

DRY = Don't Repeat Yourself

Viene a decir que cada trozo de información, idea, concepto, decisión de diseño, dato debe estar expresada al menos en un sitio y como mucho en un sitio.

Un ejemplo evidente es la creación de un método para "formatear" coordenadas geográficas, es decir pasar de un double lat = 41.32242423 a un String latStr = "41º 19' 20,73"". Es lógico expresar ese algoritmo una sola vez en el código y utilizarlo desde todos los puntos de la aplicación en los que se necesite realizar esta conversión.

Parece una obviedad, y además, por suerte, este principio es una de las cosas que primero se enseñan en las escuelas de informática, por lo que a priori nadie se atreve a discutirlo... otra cosa es la aplicación real que se hace de él y lo fácilmente que lo olvidamos muchas veces.

Ventajas de aplicar este principio:
  • Menos trabajo inicial: nos ahorramos repetir código que ya tenemos desarrollado (o datos, o documentación, o lo que sea)
  • Claridad: es más fácil entender la aplicación si cada cosa está en un solo sitio
  • Coherencia: si algo está expresado en dos lugares diferentes, es casi seguro de que habrá variaciones. Incluso si inicialmente una de las instancias es resultado del copy&paste, con el tiempo serán diferentes, cuando alguien haga una modificación en una de las instancias y se olvide de la(s) otra(s).
Oportunidades para aplicarlo:
  • Subrutinas, clases de utilidad, código, refactorización
  • Datos
  • Reglas de negocio
  • Esquemas de la base de datos
  • Planes de Prueba
  • Sistema de Build de la aplicación
  • Documentación
El libro The Pragmatic Programmer (altamente recomendable) es un perfecto tratado de cómo aplicar este principio de forma extensiva en muchos aspectos de un desarrollo de software.

Un entorno de desarrollo que lleva al extremo el principio DRY es Ruby on Rails. Merece la pena echarle un vistazo aunque solo sea por la interesante aplicación de ideas como "convención frente a configuración", "database migrations", "modelo/vista/controlador" y "los 3 entornos: desarrollo, pruebas, producción" y la aplicación obsesiva del principio DRY.

Violaciones frecuentes del principio:

Está comprobado: todos tenemos un sexto sentido para detectar violaciones de este principio. Muchas veces cuando estamos escribiendo código, creando un esquema de base de datos, introduciendo datos de prueba... tenemos la sensación de "esto ya lo he escrito antes", "seguro que esto lo tenemos ya por otro lado" , "debería crear un método común en lugar de hacer copy&paste" una especie de alarma interior que nos indica que lo que estamos haciendo no huele bien del todo.
  • Copy & paste del código (tengo que probar la herramienta CPD Copy&Paste Detector a ver que tal... lo que he visto promete)
  • Copy & paste de trozos de documentación
  • El esquema de base de datos está en 3 sitios: en la documentación, en la BB.DD. y en las clases del código que acceden a la BB.DD.
  • ¿se os ocurren mas?
Para próximos posts:
  • KISS = Keep It Simple Stupid !
  • YAGNI = You are not going to need it.