Objetivo 2: Modelización del problema a resolver

Descripción

Se trata de entender el paso de historias de usuario al diseño inicial del código, con las estructuras de datos y posiblemente constantes necesarias para comenzar la implementación. Este paso incluye un modelo del problema, con lo que es esencial comprender las historias de usuario planteadas en el objetivo 1 y partir de las mismas para poder resolver el problema.

Como en todos los objetivos hasta ahora, se considera imprescindible la asistencia a clase, ya que la metodología en la asignatura está centrada en el estudiante, y tanto para que el profesor corriga y asista al estudiante como para interaccionar con quien se encargue del desarrollo, la presencialidad presenta una serie de ventajas insustituibles.

Prerrequisitos

Se tendrá que haber avanzado en el objetivo 1 hasta haber superado los tests antes de poder entregar este.

TL;DR

En este objetivo habrá que aprender a usar domain driven design para analizar las historias de usuario, extraer usándolo los diferentes objetos y entidades existentes en el dominio del problema, y codificarlos siguiendo buenas prácticas.

Tras elegir el lenguaje de programación que se va a usar, se creará un modelo del problema en código, correctamente modularizado, aunque no necesita tener todavía ninguna funcionalidad. Estos módulos tendrán que usar las estructuras de datos más adecuadas para resolver el problema, resultado del análisis usando domain driven design, así como el interfaz de la clase o módulo que mejor permita implementar las historias de usuario existentes, evidentemente avanzando las historias de usuario que se hayan creado en el primer hito en la secuencia, al menos las que estén relacionadas con el milestone en el que se esté trabajando.

Tanto la modelización como las estructuras de datos diseñadas en sí tendrán que hacerse teniendo en cuenta las historias de usuario creadas en el objetivo 1. El desarrollo ágil está centrado en el cliente, y el código que se añada al repositorio siempre tiene que añadir valor al cliente (cuyos deseos están representados en las historias de usuario).

Explicación

El convertir los deseos de los clientes en una realidad informática implica una serie de decisiones técnicas, y en principio no tiene nada de trivial. En entornos de desarrollo ágil hay que tener en cuenta no sólo los requisitos (historias de usuario) que se conozcan en ese momento, sino también posibles cambios que pueda haber en el futuro.

En desarrollo ágil, también, se va a separar lo más posible la lógica de negocio (lo que efectivamente hace la aplicación) de su implementación completa (cómo va a aparecer al público, cómo va interaccionar con el sistema). El hacerlo así permite asegurarse de que esta lógica de negocio cumpla todos los requisitos (que están expresados en historias de usuario), y que se pueda adaptar a diferentes entornos según evolucione la aplicación.

En todo caso, hay que pasar de una historia de usuario al diseño general de algunas de las clases (o módulos) que van a intervenir en una aplicación. La metodología más aceptada se denomina Domain Driven Design, y consiste en identificar las entidades (clases que van a incluir lógica de negocio, y van a ser capaces de gestionar otras clases, así como almacenamiento y otros objetos) y objetos valor (objetos inmutables cuyo ciclo de vida va a estar controlado por objetos de otras clases).

Un elemento que siempre es importante en la modelización de un dominio es el diseño de los posibles errores o excepciones que puedan surgir; esto está relacionado con los posibles valores en la creación de un objeto y su relación entre ellos, por ejemplo. Si el sistema de tipos de un lenguaje no incluye este tipo de comprobación, tendrá que hacerse en tiempo de ejecución creando código para ello. El objeto de las excepciones es siempre tratar de dar información que sea útil tanto para el programa como para el usuario, si es que llega a ella (que en la mayoría de los casos no será así). Por eso todos los lenguajes tienen una serie de excepciones predefinidas que incluye tanto un mensaje, como datos adicionales que ayuden tanto a su comprensión por parte del programador, como a su recuperación por parte del programa. Esta modelización tiene que formar parte de este objetivo, en el cual tendrán que tenerse en cuenta estas posibles excepciones y programarse, incluyéndolas en la jerarquía más adecuada en caso de que el lenguaje (como Python) provea de una serie de excepciones estándar.

Finalmente, en el momento que vaya a haber algo de código se tiene que comenzar a seguir las mejores prácticas del lenguaje correspondiente, que incluirán una disposición de directorios determinada, forma de llamar los ficheros y las clases dentro de los ficheros, y así sucesivamente. Conviene antes de crear el fichero o ficheros informarse sobre el tema.

Sobre las historias de usuario

Las historias de usuario van a ser issues específicos, identificados como tales (mediante una etiqueta y el título del issue), en el repositorio de GitHub. Expresan los deseos del cliente, y tienen que estar siempre en el dominio del problema; es decir, expresar sólo lo que el cliente desea que se solucione, no de qué forma.

Por otro lado, cada historia de usuario tiene que tener relación con la lógica de negocio, es decir, tiene que requerir un procesamiento de datos no trivial.

Tendrá que haber tantas historias de usuario como quepan en la idea original de proyecto, planteada en el objetivo 0. Sin embargo, lo más habitual es que, dentro de los términos de la asignatura, sólo se pueda trabajar con una o dos de ellas. Sin embargo, plantear alguna más que la que se pueda abordar en la asignatura demostrará que se comprende el concepto, así que no hay por qué evitarlas.

Sobre el trabajo con issues

Qué es un issue y cómo se trabaja con él se incluye en este artículo. Los issues son herramientas imprescindibles en el desarrollo de un proyecto, y conviene entender qué hacer, para qué sirven, y cómo se van a usar.

Usar un repositorio de forma correcta no solo permite organizar el trabajo de desarrollo de forma más eficiente, sino que también contribuye a que sea más fácil colaborar con él y a la creación de buenos hábitos de trabajo colaborativo. Hay una serie de buenas prácticas, que incluyen las comentadas en el objetivo anterior, pero que además, en este objetivo, deberán de tener en cuenta lo siguiente.

soluciona #1 de la forma x

Estas buenas prácticas se tendrán que seguir en todos los objetivos subsiguientes del proyecto, empezando por este, y no hacerlo se indicará al estudiante para su mejora. También se testearán a la hora de hacer el envío.

Orden de trabajo en este objetivo

Este objetivo construye sobre el anterior. Se tienen que empezar a implementar las historias de usuario y avanzar hasta el primer hito, que podría ser perfectamente el resultado de este objetivo (ya que es una clase diseñada con todos los elementos que permitan implementarse luego). Así que

  1. Consultar el milestone e historias de usuarios asignadas, ordenadas (si lo ha hecho el autor) en un orden lógico de desarrollo. En general, debe elegirse la historia de usuario que parezca más simple o esté asignada de antemano a los primeros milestones/productos mínimamente viables.
  2. Tratar de entender, a partir de las historias de usuario, qué problemas son los que se plantean en el dominio descrito por las mismas. Cada problema se convertirá en un issue asignado (enlazado) a esa historia o historias de usuario, también organizadas de forma lógica. Los issues son siempre problemas, no tareas o soluciones al mismo; plantean un problema que se resolverá iterativamente en los comentarios al mismo, posiblemente en otros issues, y eventualmente en el código; en este caso la solución se planteará directamente en el código, y en ausencia de tests automáticos, será el product manager (la persona o personas que revisan el pull request) quien decida si esa solución es válida en función de que sea o no una modelización suficiente y mínima del problema, y que sea útil para continuar el desarrollo.
  3. Este objetivo tiene un porcentaje importante de análisis de problemas, y un porcentaje muy inferior de programación. Es esencial entender bien el dominio problema tal como está descrito y teniendo en cuenta solamente lo que está escrito, y para ello se crearán una serie de issues que vayan disgregando el problema en sus diferentes componentes, planteando los retos que representa la implementación de cada uno de ellos. La identificación de palabras clave (que forman parte de ese lenguaje ubicuo del que habla domain driven design es fundamental para su conversión en estructuras de datos, porque será lo que más adelante (objetivo 4) probaremos; estas palabras clave incluirán tanto verbos (que indicarán las funciones que se quieren hacer) como adjetivos (que nos dirán cómo comparar lo obtenido con lo que desea el cliente) como sustantivos (esenciales en esta etapa, serán las estructuras de datos sobre las que se va a trabajar).
  4. Usar esos issues como una guía para diseñar las clases (o módulos, entidades en general). En este proceso se tendrá en cuenta la metodología de diseño dirigido por dominio, decidiendo qué será un objeto valor, qué una entidad que los incluya, qué los agregados. Es esencial que se entienda el problema y sus diferentes apartados antes de comenzar a escribir código, y así tenéis que reflejarlo en los issues que creéis.

Implícitamente, en este objetivo se seleccionará el lenguaje de programación; pero el hacerlo será también un issue en el que se discutirá la elección entre todas las personas involucradas.

Importante: si en tres semanas la persona a la que se ha asignado el repositorio propio no ha asignado el PR a un milestone o revisado el PR, conviene ponerse en contacto con el profesor para asignar a otra persona.

El 50% de los estudiantes el año pasado tardó 23 días o menos en hacer la entrega. Si en este plazo no ha habido ningún contacto, es mejor solicitar un cambio para no bloquear el resto del desarrollo, especialmente si la persona que está bloqueada ya ha superado este objetivo.

Algunas reglas para escribir código limpio

El desarrollo ágil requiere que se haga un énfasis en la calidad en todos los pasos del desarrollo, así que tanto en el diseño del código como en la implementación deben seguirse una serie de reglas. Algunos consejos pueden ser:

Diferentes conceptos a tener en cuenta:

  1. Interfaz vs clase. Un interfaz tiene sólo el API. Algunos lenguajes, como Java o TypeScript, permiten crear interfaces que se tendrán que implementar en una clase concreta, sin tener ninguna lógica de negocio.
  2. Mutabilidad frente a inmutabilidad: una clase inmutable no permitirá cambiar de valor tras su creación; los objetos valor siempre son inmutables. A veces se denominan “congeladas” (frozen) o dataclasses (ya que, esencialmente, son datos, o sea, objetos que contienen sólo un valor). En ocasiones se consigue la inmutabilidad simplemente no permitiendo cambiar ningún atributo desde el API de la clase.
  3. En esta mutabilidad se tendrá que tener en cuenta el uso de variables de instancia privadas, así como sólo los getters (ningún setter). En general, conviene usar lenguajes que tengan en cuenta este tipo de distinción.
  4. Composición frente a herencia. La programación dirigida a objetos es muy rica, y mientras que algunos lenguajes permiten sólo herencia, otros permiten composición de roles o mixins (como Ruby o Raku).
  5. Tiempo de ejecución frente a tiempo de compilación: Es mejor trabajar con un lenguaje que permita comprobaciones de tipos en tiempo de compilación, sobre todo en funciones; eso ahorrará un montón de trabajo más adelante.
  6. Interfaz vs. implementación: el interfaz de una clase debe modelizarse teniendo en cuenta lo que se va a usar en la lógica de negocio, no simplemente reflejar los nombres de los atributos de la clase, que son detalles de implementación.

Los factores anteriores son comunes a casi todas las aplicaciones, y llevarían normalmente a la elección de un lenguaje que tenga comprobación de tipos en tiempo de ejecución, o tipado “fuerte” o tipado gradual. Este objetivo se cumplirá evidentemente cuando se elija un lenguaje, porque sin él no se puede crear los primeros interfaces o clases sin código (sólo declaración de métodos y funciones).

Esto lo deberéis tener en cuenta a la hora de escribir el código que resuelva el problema planteado en issues.

Configuración del entorno de trabajo

En este objetivo es en el que vas a comenzar efectivamente a usar un lenguaje de programación, aunque en este caso no sea el que vas a usar en tu repositorio. Conviene que elijas un entorno de desarrollo integrado (IDE), tal como emacs o VSCode, y lo configures para poder trabajar con él de la forma más productiva posible.

Deberás instalar también un intérprete o compilador del lenguaje de programación con el que vayas a trabajar. Tanto el lenguaje de programación como todas las bibliotecas y demás herramientas de desarrollo con las que vayas a trabajar deberían poder instalarse en el espacio de usuario; en el caso del lenguaje de programación, debería ser posible instalar la versión que uno desee, y poder cambiarla fácilmente desde línea de órdenes. Por eso conviene que busques un gestor de versiones para el mismo, y desde él instales las versiones con las que vayas a trabajar.

Esa instancia del lenguaje se usará desde el IDE, pero además tendrás que configurarlo con modos adecuados a tu lenguaje de programación, que reconozcan la sintaxis y te señalen posibles errores o te sugieran como completar nombres de variables y demás. Adicionalmente, conviene que incluyas un modo linter que compruebe y corrija errores frecuentes, siguiendo las buenas prácticas en el lenguaje de programación correspondiente. En algunos lenguajes no vendrá de serie con el modo básico del mismo, sino que habrá que instalarlo de forma adicional.

Si usas IDEs específicos del lenguaje, como los de IntelliJ, lo más probable es que esto esté todo ya integrado.

Información adicional

Se pueden consultar los siguientes temas del curso 0:

La introducción del MSDN magazine que se ha enlazado antes es bastante extensa, pero justifica bien todas las prácticas y las enlaza con otras buenas prácticas como el Single Responsibility Principle.

Entrega de la práctica

Todo el material para este objetivo se tiene que desarrollar, como siempre, en una rama. Desde esta rama se hará un pull request a la rama principal del repositorio propio del estudiante. Importante: el título de este pull request *tiene que incluir la cadena [IV-23-24] para que puedan filtrar fácilmente los mensajes recibidos. Cuando se hagan mejoras en el PR, el estudiante deberá pedir explícitamente revisiones adicionales con un comentario en el mismo que diga “Listo para revisión”.

Como en este hito el trabajo se hace sobre un repositorio ajeno, esa persona tendrá que revisar el trabajo que se ha enviado en un PR y aceptar (no fusionar). Cuando se haga, se etiquetará a JJ en un comentario donde se diga que está listo para revisión.

A este fichero se subirá (mediante un PR desde una rama) el nombre del proyecto, el autor y un enlace al pull request creado en el repositorio.

A partir de este hito, habrá que especificar ficheros y otros parámetros de configuración para test en un fichero iv.yaml que estará en el directorio principal, escrito en formato YAML. Por ejemplo, este

entidad: camino/a/loquesea.raku

Las claves, tales como en este caso entidad, tendrán un valor que será lo que se examine y se usará en un test para ver si se ha llevado a cabo correctamente alguno de los elementos del hito requeridos.

Se denomina “entidad” porque, en general, en domain driven design, es necesario llegar en esta fase a la modelización de una entidad; es muy difícil que se pueda progresar sólo con objetos valor. Sin embargo, el fichero al que se apunte podrá ser tanto una entidad como un objeto valor.

El formato YAML se usa extensivamente en DevOps, y se puede consultar sobre él, por ejemplo, en la Wikipedia. Se puede editar a mano (usando el modo correspondiente del editor o IDE) o, por supuesto, generarse usando alguna biblioteca o utilidad de línea de órdenes.

Como mínimo, y para pasar los tests, esta entrega incluirá, además de lo solicitado en las entregas anteriores:

lenguaje: node.js
entidad: src/Recordatorio.js

Por último, se aconseja al estudiante que dé de alta siguiendo las instrucciones del hacktoberfest para que la contribución del compañero pueda ser contabilizada en el mismo.

Lista de comprobación para quien desarrolle el objetivo

Antes de proponer el material de este objetivo para revisión, hazte las siguientes preguntas y, si es posible, contéstalas. Esto debe ir en el cuerpo del PR que se haga en el repo correspondiente.

Sobre la estructura del repositorio

* [ ] ¿He seguido las mejores prácticas en el nombre de las clases y ficheros y
   disposición de los mismos?
* [ ] ¿He mirado tanto reglas generales (para cualquier repo) como reglas
específicas para el lenguaje con el que se está trabajando?

Sobre el planteamiento

* [ ] ¿Se han planteado una serie de issues, asignados a las historias de
usuario pertinentes, que clarifiquen qué es lo que necesitan los usuarios e
identifiquen las diferentes partes del problema?
* [ ] ¿Los issues representan un problema, y no una tarea?

Sobre el análisis del problema

* [ ] ¿Se ha documentado qué análisis se ha hecho sobre el dominio para decir lo
   que se ha creado?
* [ ] ¿Se ha documentado por qué se ha elegido que lo creado sea un objeto valor,
   una entidad o un agregado?
* [ ] ¿He propuesto un producto mínimamente viable, que en muchos casos será un solo
   objeto valor que no dependa de ningún otro (y que sea la base de muchos
   otros)?.

Sobre el código

* [ ] ¿Todos los mensajes de commit explican el cambio, y no se
      limitan a repetir el nombre del fichero que se ha cambiado?
* [ ] ¿Los mensajes de commit siguen el formato estándar y buenas prácticas?
* [ ] ¿Se ha hecho una revisión real del código para comprobar que todos
   los atributos y funciones creadas están respaldadas por una HU?
* [ ] ¿Todos los issues creados están relacionados con a una HU?
* [ ] ¿Ha asignado el propietario/la propietaria del repositorio todos los
issues planteados al *milestone* correspondiente?
* [ ] ¿Todos los cambios en el código están asignados a un issue al que se
      referencia en un commit?
* [ ] ¿Se ha asignado al mismo milestone el PR que se ha hecho?
* [ ] ¿Es el milestone sobre el que estoy trabajando un producto
   mínimamente viable? ¿O tengo que solicitar al product manager que precise de
   qué producto se trata?
* [ ] ¿Se ha comprobado que el código entregado sea sintácticamente
correcto?

Lista de comprobación para el propietario/a del repositorio

El propietario/a del repo tendrá que incluir esta lista de comprobación en el review, sea positivo o negativo, para indicar lo que la otra persona ha cumplido o no.

Quien haya creado el proyecto estará trabajando en otro repositorio, pero es esencial su participación para que quien vaya a superar este objetivo en su repo lo supere con éxito. Lo esencial es que compruebe que el código vaya de ser útil para su objetivo 4, así que cuanto más avance quien desarrolle este objetivo en el mismo menos trabajo va a tener en tal objetivo (que pertenece a un milestone diferente). La interacción debe limitarse a la revisión del código, y si es pertinente, cambios en las historias de usuario o milestones en caso de que no estén suficientemente claros. Esto no es una sesión de preguntas y respuestas donde quien desarrolla te pregunta “qué quieres hacer” y tú lo dices (salvo en el caso del lenguaje de programación, donde tendrá que haber un consenso). Las historias de usuario creadas en el objetivo 1 deben ser guía suficiente para los dos retos esenciales en el desarrollo: ¿Qué hay que hacer ahora? Y ¿Lo que hago está bien?.

Por eso también incluyo más abajo una lista de comprobación para que la siga en su revisión del PR del compañero/a.

* [ ] ¿Los issues que ha planteado la persona son efectivamente problemas y
evidencian comprensión de las historias de usuario? Si no es así, es mejor que
se le transmita antes de que empiece a crear cualquier código erróneo.
* [ ] ¿Has comprobado que efectivamente el compañero/a ha seguido *su* lista de
      comprobación?
* [ ] ¿El código creado es una modelización válida de las historias de usuario
      creadas por ti, y te sirve como base para programar la lógica de negocio?
* [ ] ¿El código creado sigue las mejores prácticas para el lenguaje de
      programación que hayáis consensuado, tanto en identificadores, como
      nombres de ficheros, como disposición de los diferentes ficheros en el
      repositorio? Por ejemplo, ¿todos los commits se refieren a un issue y
      aportan código para resolver el problema planteado en el mismo?
* [ ] ¿He comprobado que el código compile, o que quien lo ha programado ha lo
ha hecho y documentado en el repositorio?
* [ ] ¿He añadido el PR y todos los issues que se resuelvan en él al milestone
correspondiente, que será el primero?
* [ ] ¿Cubre totalmente el milestone 0, o hará falta modelización adicional en
el objetivo 4 para poder programar la lógica de negocio?
* [ ] En resumen, ¿considero que con lo hecho puedo ponerme a trabajar
directamente en el siguiente milestone, que incluye la lógica de negocio?

Ayudaría al profesor si pones esta lista de comprobación en tu revisión del estudiante, a la hora de aprobarlo o de solicitar cambios adicionales.

Objetivo alcanzado

Los tests automáticos serán un requisito para poder entregar el material evaluable correspondiente a este objetivo, que estará superado si:

  1. Se ha organizado correctamente en el repositorio según las mejores prácticas del lenguaje de programación elegido.
  2. Se ha hecho un análisis correcto del dominio, y se han creado una o varias entidades/objetos valor a partir de issues que realmente representen el problema, en el lenguaje de programación elegido, que avancen hacia la implementación de las historias de usuario y el producto mínimamente viable del hito correspondiente.
  3. Los issues están correctamente encuadrados dentro de una HU y del primer hito (el cero), que es el que se está desarrollando ahora.
  4. Como en todos los objetivos, se debe responder a todas las objeciones que se hayan hecho en la corrección del mismo, por parte del profesor o de la persona propietaria del repositorio.

En todo caso, y como en todos los objetivos, se tendrá que esperar a que el profesor apruebe el PR en el repositorio del proyecto para considerarlo alcanzado.

Preguntas frecuentemente preguntadas

Estoy trabajando en un repositorio pero no contestan a mis preguntas

Salvo que se trate de aclaraciones sobre el milestone en el que se enmarca el objetivo, aclaraciones de qué significan las historias de usuario, o peticiones de asignar un PR al milestone correspondiente, la persona sobre cuyo repositorio estás trabajando no tiene que contestar a ninguna pregunta. Sólo tiene que evaluar el trabajo que realices cuando hagas el pull request, aprobándolo u orientándolo para que cubra las necesidades de los clientes. No tiene que contestar qué tenía pensado, porque eres quien tiene que plantear los diferentes problemas a partir de las historias de usuario, y comenzar a resolverlo. La modelización, y, en general, el desarrollo, no tiene más diálogo que el que hay sobre la calidad del código, y en ese contexto. Si quien haya creado el repo y las HUs tuviera que contestar a cada pregunta sobre cómo hacer algo, acaba antes haciéndolo por sí mismo.

Entrega y corrección

En los años anteriores, el 50% de los estudiantes que aprobaron tardaron menos de seis días entre la entrega y la superación del objetivo; el 75% unos 10 días. Este objetivo es crítico para la comprensión y la superación de la asignatura, por eso se recomienda que se le preste la máxima atención y se trate de que no transcurran más de dos semanas entre entrega y superación del mismo.

El plazo de entrega aconsejado es de siete semanas a partir del comienzo de las clases; en años anteriores, entre los que aprobaron

Valoración

El alcanzar este objetivo avanzará, en principio, 15% de la nota del apartado correspondiente de la asignatura.

A donde ir desde aquí

Si has terminado, comienza con el siguiente.