martes, 4 de febrero de 2014

Ingeniería de requerimientos

Por Hernán Gil

La captura de requerimientos no funcionales abarca el proceso de identificar y precisar las responsabilidades y las características que un sistema debe cumplir. Veamos cómo podemos utilizarla.

Tradicionalmente, el mayor centro de atención han sido los requerimientos funcionales. Sin embargo, los requerimientos no funcionales, que describen la calidad del sistema como confiabilidad o seguridad, han ganado mucha importancia dentro de la comunidad de la Ingeniería de Software en este último tiempo. Este artículo analiza en profundidad el impacto de considerar el tratamiento de los requerimientos no funcionales dentro del proceso de captura de requerimientos.


Introducción al mundo de los requerimientos

Con la evolución de la Ingeniería de Software, las exigencias sobre los sistemas son cada vez mayores. Los sistemas no sólo deben ser más complejos, asumiendo mayores responsabilidades, sino que también deben ser eficientes, rápidos, amigables, fáciles de evolucionar, etc. Este tipo de características adicionales, también conocidas como factores de calidad, están presentes en diferentes partes del sistema, lo cual dificulta su análisis en soledad. Frente a esta evolución, la Ingeniería de Software propone varios modelos de desarrollo, la mayoría de los cuales tiene las siguientes etapas iniciales: análisis de requerimientos, arquitectura, análisis y diseño. Este artículo tiene como objetivo principal el análisis de requerimientos enfocado en establecer mecanismos para identificar y manejar en esta etapa inicial los factores de calidad presentes en el sistema.

Un requerimiento define una propiedad o característica que nuestro sistema debe poseer para resolver el problema que dio origen a su desarrollo. Los requerimientos se clasifican en funcionales y no funcionales, según su propósito. Los de la primera categoría describen propiedades que el sistema debe exhibir; por ejemplo, “debe mantenerse el currículum de cada usuario” es un requerimiento funcional para un sistema que funciona como agencia de trabajo. En cambio, los requerimientos no funcionales describen cualidades del sistema; son esas características adicionales mencionadas anteriormente. 
Dentro de los requerimientos no funcionales, los más importantes son aquellos enfocados en la interacción entre el sistema y el entorno. La cantidad y el tipo de requerimientos son muy variados y pueden estar expresados a alto nivel. Los requerimientos técnicos ayudan a especificar requerimientos de alto nivel. Por ejemplo, para el requerimiento “Mejorar la seguridad de los sensores”, podemos definir dos requerimientos técnicos: utilizar un tipo especial de conexión y detectar ruido en la conexión. 
Por último, es importante mencionar aquellos requerimientos que se expresan como restricciones, ya sean técnicas (como tipo de banda ancha disponible) o relacionadas con el dominio del problema (como legislación, uso de estándares, etc.).

Una vez obtenidos los requerimientos, el próximo paso es transformarlos en código. Este proceso es claramente complejo y, además, los costos de errores en el manejo de requerimientos aumentan casi exponencialmente a medida que el proyecto avanza. Por esta razón, el proceso de identificar correctamente todos los requerimientos, como prever cambios y la evolución de antemano, es fundamental para el éxito del producto. Aquellos requerimientos que pueden ser ubicados dentro de una unidad de software, la cual asume la responsabilidad de satisfacer ese requerimiento, son más directos de manejar, y el software resultante estará bien modularizado, con claras interfaces de comunicación entre ellos, con todos los requerimientos implementados por separado. 

Sin embargo, como mencionamos anteriormente en este artículo, existen muchos requerimientos que no pueden ubicarse en una única entidad, sino que se desparraman en varias. Por lo general, los requerimientos no funcionales caen en esta categoría. El ejemplo clásico es performance, que abarca muchas entidades del sistema, y no es posible ubicarla bajo una única entidad.
Esta falta de modularización para este tipo de requerimientos es una clara alerta para nuestro sistema. La mayoría de las herramientas para capturar requerimientos están enfocadas en los funcionales, por lo que es necesario adaptarlas para que manejen también los no funcionales. Éste es el objetivo de este artículo. 

En la próxima sección analizamos las características con las que debería contar un modelo para capturar requerimientos (funcionales + no funcionales). Luego, se ejemplifican algunas técnicas tradicionales que sólo manejan requerimientos funcionales para mostrar las dificultades que surgen y las principales falencias que tienen. En la siguiente sección mostramos cómo incorporar los requerimientos no funcionales y las ventajas que esto trae. Por último, la sección de conclusiones finaliza el artículo.


Etapas iniciales de un desarrollo clásico de software.


Modelo de requerimientos

Los requerimientos no funcionales, por naturaleza, afectan a más de una entidad de nuestro sistema, y rara vez pueden ser expresados por sí mismos, sino que están asociados a otros requerimientos. Por lo tanto, un modelo que maneje este tipo de requerimientos deberá proveer de mecanismos para especificar, dado un requerimiento no funcional, los requerimientos funcionales a los cuales afecta. También, deberá existir un mecanismo que pueda mostrarle al ingeniero de requerimientos cómo se combinan para ver el resultado final. Éstas son las características “extra” que deben incluirse para manejar los requerimientos no funcionales. Aparte de ellas, existen características deseables para manejar cualquier tipo de requerimientos, que mencionamos a continuación.
Primero, los requerimientos no son fijos e inalterables, sino que están sujetos a cambios, por lo que un punto importante para nuestro modelo es que cuente con adaptación al cambio. Cuando una estrategia de requerimientos nace ya concebida para manejar cambios, la evolución del sistema será mucho más sencilla de llevar a cabo. Segundo, debe considerar el ciclo de vida de los requerimientos. Los requerimientos deben ser descubiertos, entendidos, registrados, verificados, comunicados y tratados. Es clave no saltear ninguna etapa, porque los errores en ésta se arrastrarán a las siguientes, y se convertirán, finalmente, en una seria amenaza para nuestro sistema.

Durante la etapa de entender los requerimientos, es usual encontrarnos con situaciones en las que dos o más requerimientos están en conflicto. Es importante analizar cómo resolver cada caso de conflicto. 
Otra propiedad esencial en el manejo de requerimientos, que suele pasar inadvertida, es la trazabilidad. A medida que el desarrollo avanza, muchas veces se pierde la asociación entre los requerimientos y las entidades de software responsables de satisfacerlos. Esto trae numerosos problemas, en especial, a la hora de introducir modificaciones y cambios. Mantener esta asociación en todo momento, saber en cada instante del proceso qué porción de código afecta qué requerimiento, ahorrará, seguramente, numerosas dificultades.
No todos los requerimientos tienen la misma importancia. Saber distinguir cuáles son vitales y cuáles son sólo “sugar” para el sistema es central a la hora de dedicar recursos y esfuerzos para cada requerimiento. Sin dudas, invertir tiempo y recursos en un requerimiento que finalmente tendrá un impacto mínimo no es para nada aconsejable.

Una clasificación posible utilizada en la industria es dividir los requerimientos, tanto funcionales como no funcionales, en: esenciales, importantes y deseables. 
Por último, para interactuar con el cliente y los responsables finales del sistema, es deseable usar una herramienta gráfica o, al menos, una que sea directa y simple de entender, o bien que esté expresada en lenguaje natural, con pasos sencillos y claros.


Yendo de los requerimientos al código.

Mecanismos tradicionales

Los mecanismos usuales para capturar requerimientos están enfocados en los requerimientos funcionales, y dejan de lado los no funcionales. Dada la importancia creciente de los requerimientos no funcionales, no considerarlos trae cada vez peores consecuencias. En esta sección ilustramos estos problemas bajo la visión de dos de estas técnicas, como son la especificación por casos de uso y la especificación por puntos de vista.


Manejo de requerimientos.


Casos de uso

Los casos de uso representan una técnica utilizada para capturar y modelar requerimientos. Cada caso de uso es una descripción del comportamiento que debe exhibir el sistema ante determinadas situaciones. Se especifican actores (seres humanos u otros sistemas) que interactúan con el sistema, y para una acción determinada se muestra el comportamiento esperado. En la [Figura 4] se presenta gráficamente la descripción de un caso de uso para dar de alta un pedido en un sistema de proveedores, con dos actores, un usuario del sistema y un sistema externo. Allí se ve que, en algunas situaciones dentro de este comportamiento, se requerirá la funcionalidad de otro caso de uso, denominado Almacenar BD (se incluye la palabra reservada require para indicar que sólo en algunas situaciones y no en todas se invocará este caso de uso).

Almacenar BD es un caso de uso que describe un requerimiento no funcional, como persistencia. En todas las operaciones y transacciones que se deban persistir estará presente este caso de uso. Este comportamiento estará repetido en todos los casos de uso que reflejen esas operaciones, para mostrar una clara falta de modularización. Seguramente, ésta no es la manera en que el cliente expresó cómo debía comportarse el sistema, sino que más bien podría haberle dicho al Ingeniero de Requerimientos algo parecido a la siguiente frase: “Todas las operaciones que involucren pedidos deben ser almacenadas en un archivo”. Sin embargo, al trasladar esta regla con los mecanismos que cuentan los casos de uso se recae en comportamiento repetido, debido a la falta de modularización.


Puntos de vista

Esta técnica, conocida como ViewPoints, busca separar las responsabilidades y funcionalidades del sistema, de una manera similar a los casos de uso. Considera la información relacionada con el sistema desde diferentes agentes (por ejemplo, los distintos tipos de usuario del sistema), los cuales tienen distintas e incompletas perspectivas del problema, todas igualmente válidas. Estas perspectivas parciales dan lugar a diferentes responsabilidades, metas o interpretaciones. Un punto de vista resulta de la combinación de un agente y su visión del sistema. Los puntos de vista ayudan a controlar la complejidad del sistema, separando la funcionalidad de cada agente. Una de las herramientas que implementa este modelo es PREView (en.wikipedia.org/wiki/PREview).

A diferencia de los casos de uso, esta técnica cuenta con un mecanismo nativo para manejar requerimientos no funcionales, llamados concern templates. En cada punto de vista se pueden enumerar los requerimientos no funcionales involucrados. Éste es un mecanismo adecuado cuando los requerimientos no funcionales son escasos. Cuando son muchos, este precario mecanismo de modularización se vuelve confuso, y todos los requerimientos se enredan. Una vez más, nos encontramos en una situación en la que existen requerimientos que no están correctamente especificados, por falta de mecanismos adecuados en el modelo para capturar requerimientos.


Caso de uso para dar de alta un pedido.


Análisis

Como vimos en estos ejemplos presentados, la falta de modularización de los requerimientos no funcionales se convierte en un problema grave. Varias de las características que establecimos para un modelo de requerimientos se ven en mayor o menor medida afectadas. La trazabilidad es una de las más comprometidas, ya que los requerimientos no funcionales resultan dispersos por todo el sistema. También se ve afectado el principio de adaptación al cambio, porque es imposible especificar el impacto en el cambio de un requerimiento no funcional.

Ambas técnicas comparten la estrategia de separar los requerimientos según su funcionalidad. Los requerimientos no funcionales, en cambio, no están aislados, sino que se distribuyen por sobre los requerimientos funcionales. En el caso de puntos de vista, un requerimiento no funcional estará disperso por dos o más vistas. 
En casos de uso, existe comportamiento repetido en varios de ellos. Ante estas falencias, es preciso incorporar nuevos mecanismos que logren modelar los requerimientos no funcionales. 
La siguiente sección dilucida varios indicios para llevar a cabo esta tarea.


Falencias en los mecanismos tradicionales.

Incorporar requerimientos no funcionales

Para incorporar requerimientos no funcionales, lo principal es trasladarlos como entidades de primer nivel en nuestro modelo. Es decir, deben tener los mismos privilegios y atención que los funcionales. Para cada uno, se deberá especificar sobre qué otros requerimientos actúa y cómo lo hace. 
Como contrapartida, es conveniente detallar en cada requerimiento funcional los requerimientos no funcionales involucrados. Para un mejor manejo de toda esa interacción, es preferible una herramienta visual que permite ir viendo con más claridad la interconexión entre los requerimientos. De esta manera, queda claramente establecida la forma en que los requerimientos se relacionan. Esta facilidad alivia la tarea de encontrar conflictos entre los requerimientos.
Otra herramienta que puede resultar de mucha utilidad es aquella encargada de combinar la especificación de todos los requerimientos, para ver el comportamiento final. Para ilustrar cómo se podría llevar esto a cabo, retomemos el ejemplo del requerimiento no funcional de persistencia mencionado en la sección de casos de uso. La regla para el manejo de persistencia establecía que “Todas las operaciones que involucren pedidos deben ser almacenadas en un archivo”. 
Una manera de detallar este requerimiento no funcional es a través del siguiente esquema:

> Nombre: Persistencia
> Afecta: Operaciones y transacciones
> Prioridad: Esencial
> Comportamiento: Toda operación que maneje pedidos será almacenada en un archivo al finalizar su ejecución.

Por otro lado, el requerimiento para dar de alta un pedido puede ser descrito de la siguiente manera:

> Nombre: Alta Pedido
> Tipo: Operación
> Afectado por: Persistencia
> Prioridad: Esencial
> Comportamiento: El sistema debe permitir que un cliente o usuario ingrese un pedido.

Por último, una herramienta de combinación tomará ambas especificaciones y generará automáticamente una nueva descripción del comportamiento, que incluya el comportamiento de ambas, tal como se muestra a continuación:

> Nombre: Alta Pedido con Persistencia
> Prioridad: Esencial
> Comportamiento:
                 El sistema debe permitir que un cliente o usuario ingrese un pedido.
                 El pedido será almacenado en un archivo de pedidos.

De esta manera, además de tener todos los requerimientos modularizados, podemos observar cómo resulta la composición. Esto es de gran utilidad, ya que podemos observar por adelantado cómo funcionará el sistema y tomar decisiones según los resultados obtenidos. Esta combinación puede servir también como molde general para producir casos de tests.

Otra posibilidad bastante simple, utilizada comúnmente para ver la interacción de requerimientos, es ubicarlos en una matriz. Para cada requerimiento, se marca con qué otros tiene relación. La matriz de requerimientos permite observar con naturalidad tanto conflictos como impacto de cambio. Asimismo, la trazabilidad de elementos a código se mantiene clara, organizada a través de la matriz.

Mediante simples mecanismos de separación y composición, los requerimientos no funcionales son especificados como entidades de primer nivel, y su modularización es adecuada. Luego, en futuras etapas de implementación, se pueden seguir estrategias de desarrollo basadas en separación y combinación de conceptos, como Programación orientada a aspectos, o Hiperslices, como también utilizar estrategias más populares, como la Programación con objetos. 
La trazabilidad será más directa en las primeras, pero la especificación de los requerimientos no está involucrada directamente en la manera en que se programarán y codificarán los requerimientos, a menos que, explícitamente, uno de los requerimientos o alguna restricción así lo indique (por ejemplo, un cliente que por alguna razón de compatibilidad exige que su sistema sea compatible con un motor de bases de datos en particular). 

La ingeniería de requerimientos trata de descubrir qué se debe hacer, y no cómo hacerlo. Algunas herramientas para capturar requerimientos, incluyendo los no funcionales, son AORE with ARCADE y Cosmos. También existen extensiones a los casos de uso para contemplar requerimientos no funcionales.


Mecanismos para requerimientos no funcionales.


Conclusiones


Los requerimientos describen las propiedades que debe tener un sistema de software. El proceso de identificarlos y verificar que el conjunto de requerimientos obtenido sea realmente lo que desea el cliente no es algo trivial. Y esta situación se ve aún más afectada debido a que los requerimientos no funcionales, que describen la calidad del software final, no son modularizados completamente en los modelos usuales para la captura de requerimientos. 
En este artículo ejemplificamos esta situación, a través de dos conocidas técnicas: casos de uso y puntos de vista. 
Previamente, ya pensando en incorporar los requerimientos no funcionales, describimos las características que idealmente debía tener nuestro modelo. Por último, observamos cómo se puede incluir este tipo requerimientos, a través de mecanismos que permiten especificar cómo interactúan y relacionan todos los requerimientos, generalmente ayudados por herramientas automáticas. 
Esta completa modularización genera un documento de requerimientos completo, expresado de manera natural, y fácilmente entendible por todos los usuarios y responsables del sistema. De la misma manera, esta organización da lugar a que todos los elementos que surgen en el requerimiento puedan ser ligados a elementos de código que implementen esa funcionalidad. Esto impacta directamente en una evolución más sencilla de nuestro software.