archivo

Archivo de la etiqueta: casos de uso

Las relaciones extend aportan funcionalidad al comportamiento definido en el caso de uso al que extiende cuando se verifica una determinada condición (que “activaría” el punto de extensión).

Una vez finalizado el caso de uso que extiende se vuelve al punto de extensión y continua la secuencia definida en el escenario, es decir, el viaje del extend es de ida y vuelta, como también lo es el de include, la diferencia es que el primero es opcional (se tiene que cumplir una condición) y el segundo se ejecuta siempre.

Es importante diferenciar los extend de los escenarios de error o de excepción donde se interrumpe el flujo del escenario que se está ejecutando y se pasa a un nuevo escenario donde se ejecuta otra secuencia de pasos que no tienen por qué volver al escenario que llevó a él.

Hace poco leí que de la misma manera que el mapa de una ciudad no es la ciudad, un diagrama de casos de uso no pueden sustituir a la descripción de los mismos.

La descripción de sus escenarios es lo realmente importante, aunque los diagramas proporcionan un apoyo importante para entenderlos.

Los casos de uso van un paso más allá de los requisitos, ya que representa interacciones entre el sistema y los actores y si se quiere que sean de utilidad deben poder ser entendidos por usuarios, aunque estos necesiten el apoyo de personal técnico, de los diagramas de casos de uso y de los diagramas de flujo que se puedan obtener a partir de la definición de las secuencias de pasos de sus escenarios.

Si la definición de casos de uso no se entiende, no sirve. Mucho esfuerzo para nada.

Cuando nos planteamos una estrategia de desarrollo basada en ciclos de vida iterativos e incrementales, como tenemos por ejemplo en las metodologías ágiles, se suele plantear la entrega de evoluciones del producto en períodos constantes de tiempo. Esto es lo que en algunas metodologías se conoce con el nombre de sprint.

La cantidad de trabajo que cabe en una entrega no deja de ser una combinación de estimaciones, ya que por un lado tenemos el parámetro velocity, que constituye la cantidad de unidades de trabajo que puede asumir el equipo de proyecto en una iteración (y que no podemos considerar estable, hasta que se hayan completado algunas de ellas) y por otro la estimación de esfuerzo de las diferentes historias de usuario o casos de uso que se van a implementar en este ciclo, pudiéndose utilizar para ello diferentes técnicas, como por ejemplo Planning Poker.

Contingencias en un proyecto de desarrollo de sofware puede haber muchas, así como errores en las estimaciones, por lo que no siempre será posible implementar lo previsto en cada iteración. Ante esto, hay diferentes opciones, por un lado está el overtime y será el equipo de proyecto en consenso con el cliente el que determine si merece la pena invertir ese tiempo extra, debiéndose tener en cuenta si se ha acudido recientemente al mismo.

Kent Beck cuando describe los principios y recomendaciones de la programación extrema indica que el overtime es un recurso que se debe utilizar solo en causas muy justificadas y de manera muy espaciada y yo estoy totalmente de acuerdo con él.

Otra opción será replanificar la fecha de entrega o entregar los hitos cerrados a la fecha de finalización del ciclo. La metodología en unos casos determinará qué camino utilizar. Soy de la opinión de que si las iteraciones se producen en períodos constantes de tiempo, no se debe romper el ritmo, sin embargo si no lo son, sí que se puede abrir la puerta a la replanificación.

También habrá que tener en cuenta las demandas del proyecto y el tiempo entre iteraciones. Por ejemplo, si no ha dado tiempo de corregir una incidencia y la siguiente iteración no es hasta dentro de cinco semanas, tal vez sí que habría que plantear un cierto retraso en la entrega.

Opciones hay muchas. Precisamente hoy he estado hablando de este asunto con algunos compañeros. Hemos consensuado que la solución con menos overhead y más flexible, es utilizar un documento por cada tipo de documento del proyecto, es decir, un catálogo de requisitos, un documento de casos de uso o cualquiera que se utilice en función de la metodología y del tipo de proyecto, siendo extensible por ejemplo a historias de usuario, diagramas de despliegue, etc… que vaya creciendo de manera incremental con cada evolución, pero que en contenido vaya creciendo de manera diferencial.

Es decir, el documento contiene el contenido de las iteraciones anteriores y el de la actual, pero en la parte dedicada a la actual solo se incluye lo correspondiente a esta evolución y solo recoge de las anteriores lo estrictamente necesario para ayudar a la comprensión de la misma.

Un desarrollo de un módulo, componente, clase, etc… es el resultado de una especificación realizada por parte del área usuaria o del área técnica del cliente (de manera directa o intermediando entre usuarios y proveedores).

La especificación puede ser recogida documentalmente (que puede ser más o menos formal) o no y puede contener mayor o menor detalle en función de la metodología que se utilice y del tipo de proyecto.

Entre las estrategias más comunes para recoger esas especificaciónes, tenemos los catálogos de requisitos, los casos de uso (que pueden ir acompañados de una recogida previa de requisitos o no) y las historias de usuario.

Las historias de usuario son utilizadas principalmente en desarrollos ágiles, ya que por su propia naturaleza, como veremos a continuación, resultan muy adecuadas para este tipo de metodologías.

Es importante señalar que los casos de uso constituyen una alternativa igualmente válida, pero hay que tener en cuenta que requieren un grado de formalidad mayor, necesitan un mayor esfuerzo para construirse y mantenerse y necesitan la participación de personal técnico tanto para realizar sus diagramas como las especificaciones de los mismos.

Describir en qué consisten las historias de usuarios resulta sencillo, ya que se basa en describir en pocas frases, utilizando el lenguaje del usuario, una especificación o un requisito software. Sobre esa definición su implementación se puede realizar de formas muy diferentes: fichas, post-it, etc…

Suponen, por tanto, un esquema de trabajo muy flexible, gracias a esa ausencia de formalidad. Si hay que realizar modificaciones en las mismas, basta con enmendar la historia de usuario anterior, crear una versión nueva, etc…, en cualquier caso se requiere muy poco esfuerzo para hacer las correcciones oportunas.

Existen metodologías, como la programación extrema, donde se requiere que las historias de usuario, sean escritas por ellos mismos, ¿quién mejor?.

Se requieren pocos condicionamientos a la hora de considerar como válida una historia de usuario, sin embargo para que sean efectivas deben cumplir la mayor parte de las siguientes características:

– Se debe poder estimar el esfuerzo que requiere su desarrollo de la manera más simple posible, esto debe limitar el alcance y tamaño de la historia de usuario, debiendo ser descompuesta en casos más simples en el caso de que sea necesario.

– Se deben evitar solapamientos funcionales, de manera que cada historia de usuario exprese un requerimiento concreto.

– Una historia de usuario debe poder ser discutida entre usuarios y equipo de proyecto, con el objeto de poder perfilarla, lo que puede dar lugar a diferentes versiones de la misma antes de empezar su construcción o a redefinirla o modificarla en posteriores iteraciones.

– Debe poder ser verificable, de manera que una vez implementada la funcionalidad en la aplicación, se compruebe si efectivamente lleva a cabo la especificación descrita.

En el entorno de personas con las que trabajo existen serias dudas de que la metodología de desarrollo guiado por las pruebas o Test-Driven Development, sea ágil, en el sentido de que entienden que si se aplica de manera ortodoxa o si bien las pruebas unitarias se desarrollan después, se pierde capacidad de trabajo efectivo por parte del equipo de proyecto.

Nunca he participado en un proyecto que siga esta metodología y en los desarrollos en los que he trabajado la importancia que se le da a las pruebas unitarias es muy escasa, por lo que no puedo aportar mi experiencia para opinar sobre las bondades o no de esta metodología, de manera que me tendré que centrar en lo que he leído sobre ella.

No obstante, hay que tener en cuenta una cosa. Agilidad no es lo mismo que desarrollar más rápido o por lo menos no es su objetivo principal, ya que si bien es cierto que la aplicación de metodologías ágiles, eliminan muchos aspectos que son prescindibles en un proyecto de desarrollo de software y por lo tanto pueden optimizar tiempos de desarrollo, el objetivo último de las mismas es conseguir productos software de calidad, que proporcionen satisfacción al cliente, dentro de unos plazos y presupuestos definidos (siempre y cuando, ambos sean realistas y acordes a la naturaleza del proyecto y al nivel de calidad que se espera del producto software), es decir, que no se produzcan los factores que derivan en la crisis del software.

El desarrollo guiado por las pruebas, fue introducido (o reintroducido) por Kent Beck, creador de la metodología de programación extrema y en cierto modo la segunda es una consecuencia natural de la primera, si bien ambas pueden utilizarse sin problemas por separado.

La metodología TDD a grandes rasgos es fácil de exponer:

– Se construyen las pruebas unitarias (o se puede extender si se desea el concepto a otros tipos de pruebas de más alto nivel, siempre y cuando sea automatizable su ejecución).

Como estos casos de prueba están construidos antes desarrollarse el componente software sobre el que se va a trabajar, van a fallar a la primera. De hecho se considera necesario verificar que fallan porque si no es así, querría decir que el test está más construido (¿por qué no?) o que el componente software ya está desarrollado (en proyectos donde se trabaja con sistemas de mediana o gran envergadura y con un número de desarrolladores alto, puede pasar que el componente o módulo ya se haya implementado previamente, bien porque le ha hecho falta a un compañero para otra funcionalidad o bien porque no se ha verificado que efectivamente ese componente ya estaba desarrollado).

Para la construcción de los casos de prueba, los desarrolladores deben basarse en las especificaciones indicadas en los casos de uso o en las historias de usuario. Ahí radica la importancia que tiene la construcción de las pruebas unitarias antes del desarrollo del componente, ya que implica un mayor enfoque en los requisitos, una mayor necesidad de comprender la funcionalidad y comportamiento deseado, es decir, implica comprender el problema, conocer los resultados esperados, antes de empezar a desarrollarlo.

La ventaja de desarrollar las pruebas antes de implementar los componentes es la indicada en el párrafo anterior, es decir, enfrentarse y comprender el problema antes de empezar a resolverlo.

Si las pruebas unitarias se construyen después, no estaríamos hablando de TDD, eso es importante, por lo que lo repito, si las pruebas unitarias se construyen después se trata de otra estrategia de desarrollo de software, no de desarrollo guiado por las pruebas.

Pese a que como he comentado, no se ha fomentado en los proyectos en los que he participado la construcción de pruebas unitarias, soy de la opinión de que es favorable tener una buena cobertura de código, por lo que la existencia de pruebas unitarias, antes o después de implementar los componentes, permite detectar de manera rápida la existencia de efectos colaterales o desarrollos erróneos de métodos o clases.

¿Por qué no lo hemos fomentado? Es una suma de varios factores, por un lado pese a que entendemos los beneficios que puede proporcionar, no tenemos del todo claro su retorno de la inversión, pese a que es objetivo que la deuda técnica se reduce en proporción a la cobertura, por otro lado, los proveedores con los que hemos trabajado tampoco utilizan esta práctica en sus desarrollos. Por tanto y en resumen, la falta de experiencia nuestra y de los proveedores hace que nos más dar el paso y prioricemos otros aspectos en el proceso de desarrollo y en la calidad del diseño y de la codificación.

– A continuación se construye el componente software que debe verificar las reglas de funcionamiento definidas en las pruebas.

Una vez desarrollado, se pasa el test y si funciona adecuadamente, se pasa de nuevo todo el juego de pruebas, si hay errores, se vuelve a revisar el componente, si todo ha ido bien, se pasa a la siguiente fase.

– Una vez que se tiene un software que verifica las pruebas implementadas, se procede a su refactorización.

El objetivo de la utilización de esta técnica es evidente, desarrollar software con el menor número de fallos posible, reduciendo además los errores debidos a efectos colaterales en el desarrollo, respetando también la calidad de diseño y de codificación para favorecer la comprensión y capacidad de mantenimiento del software.

Se sabe que cuanto más tarde se detecten los errores más esfuerzo se requiere para solucionarlo, esta técnica pretende disminuir el riesgo por la presencia de errores de ese tipo, además de facilitar un código donde sea mucho más fácil trabajar, tanto para corregir esas incidencias como para contruir nuevas funcionalidades o modificar, si fuera necesario (el software es de naturaleza adaptativa), las ya implementadas.

Espero poder probar más pronto que tarde esta metodología en alguno de los proyectos en los que participo y poder verificar personalmente las ventajas que tiene la utilización de esta estrategia de desarrollo.

En su día describí en que consistía el ciclo de vida en cascada y las consecuencias del mismo. Podría ponerme a escribir mil artículos sobre el tema y no llegaría a expresar todo lo perjudicial que ha resultado y resulta para el desarrollo de software.

Cierto es que todos los desarrollos son cascadas, en el sentido de que en mayor o menos medida hay un análisis, un diseño, una construcción y una implantación, pero también lo es que la secuencia de fases sean esas u otras no son las responsables del problema (salvo que la elección no sea adecuada para el proyecto).

El problema es la duración de cada ciclo completo y el ciclo de vida tradicional o en cascada se basa en el desarrollo del sistema completo o la mayor parte del mismo en una iteración y esto es ir contra la naturaleza de los proyectos donde el trabajo es adaptativo y no puede estar basado en la predecibilidad de un análisis que se ha realizado desde la abstracción y desde la interpretación de un problema por parte de usuarios y de los analistas.

Es inevitable la abstracción y la interpretación y pese a que se pueden aplicar diferentes técnicas (prototipado, casos de uso, historias de usuario, etc…) que ayudan a que las distintas partes a homogeneizar la interpretación de las funcionalidades, cuanto mayor sea el abanico de las mismas con las que se trabaja, mayor será la posibilidad de que se hayan escapado detalles (que pueden ser más o menos importantes) o que simplemente cambien, aparezcan otras nuevas o se cambien las prioridades en medio del proyecto.

Si en un desarrollo en cascada de un tamaño medio o superior, a lo largo del desarrollo no se presentan cambios en las especificaciones resulta casi peor que el hecho de que no aparezcan, ya que probablemente los problemas se presenten de golpe cuando los usuarios empiecen a utilizar el sistema, lo que puede resultar crítico para la propia subsistencia del sistema y por tanto para la inversión realizada (sin embargo en función del tipo de procesos que se haya informatizado la pérdida puede ir más allá de la inversión, sobre todo si hay en juego otros aspectos que dependen del buen funcionamiento de la aplicación).

No quiero decir que un proyecto desarrollado con un ciclo de vida en cascada no pueda tener éxito, lo puede tener en mayor o menor medida, sin embargo su propia dinámica de funcionamiento, su propia naturaleza, son un obstáculo muy complicado de solventar y una fuente continua de problemas y desgaste entre las diferentes partes que participan en un proyecto y probablemente ninguno termine de estar satisfecho porque todos deberán haber dado concesiones para sacarlo adelante.