viernes, 25 de julio de 2008

User Stories

Las User Stories describen funcionalidades de un sistema de software que aportan valor al usuario y/o al cliente. Una User Story está compuesta por:
  • Una descripción escrita de la funcionalidad, usada para planificar y como recordatorio (normalmente se escriben en post-it y se pegan en pizarras para que el equipo las tenga presentes mientras está desarrollando)
  • Conversaciones sobre la story, que sirven para despejar las dudas y los detalles de la funcionalidad
  • Pruebas de aceptación (UAT), que sirven como documentación y pueden ser usadas para determinar cuándo una story está completa y libre de bugs.
Las User Stories son la unidad más pequeña de incremento del sistema y la unidad de estimación y control en un entorno ágil. Incluye los objetivos y motivaciones del usuario. Un caso de uso normalmente es más grande que una user story. Por ejemplo, cada camino dentro de un caso de uso podría ser una user story.

Descripción Escrita

Es importante entender que los post-it o tarjetas con la descripción escrita son sólo una parte muy pequeña de una user story. La parte más importante es la conversación, donde el usuario le explica al desarrollador qué es exactamente lo que quiere y los detalles de la funcionalidad. El desarrollador podría escribir anotaciones en la parte trasera del post-it, pero si lo hace tiene que ser algo cortito, una pequeña anotación de pocas palabras, nada más.

Un ejemplo de descripción escrita para user story podría ser:

Como buscador de trabajo quiero poder subir mi curriculum a la página para que las consultoras me llamen si están interesadas.

Es necesario que conste el para qué en la descripción escrita, ya que esto permite tomar decisiones con respecto al diseño, a cómo resolver el problema. Si el usuario no explica para qué quiere esa funcionalidad, es probable que el desarrollador no entienda toda la perspectiva del requerimiento y no codifique lo que el usuario necesita.

Como <rol de usuario>, quiero <función del sistema> para poder <valor de negocio>

También es importante que el rol del usuario esté bien especificado. Poner un usuario genérico o un usuario muy específico son errores muy comunes a la hora de escribir los post-it. Un post muy interesante del blog de Assembla titulado "Do's and Dont's for User Stories, Use Cases, Scenarios" trata sobre estos temas. Básicamente lo que dice es que si tenemos problemas a la hora de la construcción, tenemos que tener bien claro a quién podemos llamar para resolver alguna duda que se haya presentado sobre la funcionalidad o el negocio, pero tampoco es bueno que el usuario sea demasiado específico, por ejemplo Pepe, porque la agenda de Pepe puede ser estrecha y puede que no sea fácil encontrarlo. Además Pepe no existe como rol dentro del producto. Pepe es un individuo, la instancia de un rol. Cuando escribimos las user stories hablamos de roles de usuario.

Pruebas de Aceptación

Las user stories son notas, recordatorios de una conversación. ¿Cómo termina la conversación? Puede ser en un documento, en una pantalla, en un prototipo, pero nada de esto es obligatorio. Lo importante es que quede definido cómo se va a aceptar y para esto las Pruebas de Aceptación automatizadas son ideales.

¿Por qué automatizadas? Porque para "abrazar al cambio" (lema fundamental del manifiesto ágil) debemos reaccionar de forma rápida ante él. En un negocio donde los cambios se dan constantemente y el código es refactoreado todos los días, las pruebas de regresión se multiplican exponencialmente. Tediosa y muy susceptible a errores sería la tarea de un tester si todos los días tuviera que probar lo mismo de forma manual.

Cuando un miembro del equipo está conversando con el usuario sobre una user story, un tipo de anotaciones que puede hacer en la cara opuesta del post-it es el de recordatorio sobre cómo testear la story. Por ejemplo, uno podría escribir:
  • Probar subir un curriculum dañado
  • Probar subir un curriculum vacío
  • Probar dejar vacío el campo de Nombre
  • Probar dejar vacío el campo de Remuneración Pretendida

Dividir una User Story épica

Cuando una user story es demasiado grande se dice que es épica. Programar una user story épica podría llevar un mes, un año, lo que sea; la cuestión es que seguro va a tomar más tiempo que la duración de una iteración (pequeñas para cualquier metodología ágil) y va a resultar imposible estimarla. Por eso las user story épicas hay que dividirlas.

Algunos criterios de división pueden ser:
  • Por Datos: Por ejemplo, si tengo un formulario web podría empezar con pocos datos. Podría construir los campos de Nombre del Postulante, Teléfono y Remuneración Pretendida. Y más adelante agregar otra user story con los campos que faltan.
  • Por Casos Especiales: Por ejemplo, si tengo un caso especial de que se tiene que registrar un extranjero, pero necesita llenar unos datos especiales y hay que dar de alta otros registros. El caso de alta de los extranjeros puede ir para otra user story.
  • Por Operaciones: Se podría dividir la Alta, Baja y Modificación en tres user stories diferentes.
  • Por Temas cross y no funcionales: Podría empezar con una implementación que no maneje seguridad, log, manejo de errores, performance, volumen, e irlos agregando en siguientes iteraciones.
  • Por Prioridad: No es bueno tener una user story que abarca funcionalidad de distintas prioridades. Por ejemplo: "Como vendedor de Cta Cte quiero poder dar de alta y buscar facturas para poder almacenarlas y después imprimirlas como indica la ley". Probablemente el dar de alta sea imprescindible, pero el buscar se pueda dejar para otra iteración. Cuando las prioridades están mezcladas, se termina invirtiendo tiempo en funcionalidades de baja prioridad, dejando sin hacer otras de mayor prioridad.
Al dividir una user story hay que tener en cuenta cuáles son los Minimum Marketable Features del producto. Esto es: el conjunto de mínimas funcionalidades con la que el producto puede instalarse en producción.

¿Qué tan larga tiene que ser una User Story?

Recién vimos criterios de división para cortar una user story para que fuera estimable y que pudiera construirse en una iteración. Sin embargo, no es imprescindible llegar a que la user story tenga un detalle atómico.

En general, no tiene sentido dividir una user story:
  • Por debajo de un tiempo estimado de 2 o 5 días (pensando en iteraciones un poco más largas, de dos o tres semanas)
  • En tareas: Hay que dividir por funcionalidad, no por tareas!

User Stories Efectivas

Así como para UML tenemos buenas practicas para escribir Casos de Uso Efectivos, también existen para User Stories y están descriptas en detalle en el libro de Mike Cohn: "User Stories Applied For Agile Software Development".

Cohn dice que una buena User Story debe ser:
  • Independiente: No debe depender de otra user story.
  • Negociable: Las user stories no son obligaciones contractuales, son simplemente descripciones de funcionalidades, que pueden cambiar o negociarse en cualquier momento.
  • Valiosa para el Cliente o el Usuario: Consideremos una user story de este estilo: "El software debe estar escrito en C++" o "El programa se conectará a la Base de Datos a través de un pool de conecciones". Al usuario realmente no le interesan estos detalles. Éstas no son user stories.
  • Estimable: La funcionalidad que describe tiene que tener un tamaño que un desarrollador pueda estimar. Hay tres razones comunes por las que una user story puede resultar imposible de estimar: 1) Falta de conocimiento del dominio por parte de los desarrolladores, 2) Falta de conocimiento técnico por parte de los desarrolladores, 3) La user story es demasiado grande.
  • Pequeña: Por todo lo que ya estuvimos viendo.
  • Testeable: Deben poder ser testeadas para que los desarrolladores puedan asegurar que la funcionalidad está completa y funcionando correctamente (por lo general por medio de los UAT). Ejemplos de user stories no testeables: 1) "Como operario quiero que el software sea fácil de usar...", 2) "Como data entry quiero nunca tener que esperar demasiado para que una pantalla aparezca...". ¿Qué es fácil de usar? ¿Qué es esperar mucho?

Responsabilidades de los Desarrolladores

Los desarrolladores son responsables de:
  • Ayudar al cliente a escribir user stories efectivas, y conseguir toda la información necesaria mediante la conversación y quizá algunas de las técnicas de relevamiento vistas en mi post de Team Skill 2: Entendiendo las Necesidades del Usuario, como Prototipos, Demos o Storyboards.
  • Si se ven tentados a preguntar o incluir una story sobre el uso de una tecnología o una pieza de infraestructura, por ejemplo, intentar describir la necesidad en términos de valor para el usuario o el cliente.
En Scrum, por ejemplo, estas responsabilidades caen sobre el rol del Scrum Master o de algún analista del equipo.

Responsabilidades de los Clientes

Los clientes son responsables de:
  • Escribir stories efectivas, que definan funcionalidad de tamaño apropiado, independientes, generen valor para el usuario o para él mismo (es responsable de que el producto tenga algún sentido) y de que sean testeables.
En Scrum, por ejemplo, estas responsabilidades caen sobre el rol del Product Owner.

@Override

Si una clase base de Java tiene un nombre de método varias veces sobrecargado, redefinir dicho nombre en la clase derivada no ocultará la implementación en ninguna de las versiones de la clase base (a diferencia de lo que sucede en C++). Por lo tanto, el mecanismo de sobrecarga funciona independientemente de si el método ha sido definido en este nivel o en una clase base.

Lo más común es sobrecargar los métodos del mismo nombre, utilizando exactamente la misma signatura y el mismo tipo de retorno. En caso contrario, el código podría resultar confuso (lo cual es la razón por la que C++ oculta todos los métodos de la clase base, para que no cometamos lo que muy probablemente se trata de un error).

JavaSE 5 ha añadido al lenguaje la anotación @Override, que no es una palabra clave pero puede usarse como si lo fuera. Cuando queremos sustituir un método, podemos añadir esta anotación y el compilador generará un mensaje de error si sobrecargamos accidentalmente el método en lugar de sustituirlo.

class Madre {
  public void printHolaMundo() {
   System.out.println("Hola mundo");
 }
}

class Hija extends Madre {
  @Override
  public void printHolaMundo(String nombreDelMundo) {
   System.out.println("Hola mundo " + nombreDelMundo);
 }
}

Si quisiéramos compilar este código, el compilador lanzaría el siguiente error:

"the method printHolaMundo(String) of type Hija must override or implement a supertype method"

La anotación @Override evitará, así, que sobrecarguemos accidentalmente un método cuando no es eso lo que queremos hacer.

(El texto fue extraído del "Piensa en Java" de Bruce Eckel, 4ta Edición. El ejemplo no.)

domingo, 20 de julio de 2008

Los siete Principios de Lean (o los siete Mitos de la Ingeniería de Software)

Lean Software Development es una filosofía para el desarrollo de software que surgió en la década de 1990, cuyos principios abrevan de las experiencias del TPDS (Toyota Product Development System), una filosofía que le sirvió a Toyota para desarrollar nuevos productos y que fue adaptada por Mary y Tom Poppendieck, que a su vez es una derivación de la metodología original de Toyota usada para manufactura y operaciones llamada TPS (Toyota Production System). A su vez, todas las filosofías de Toyota derivan del gurú y padre de la calidad William Eduards Deming. Hoy en día, Lean Software Development está teniendo nuevamente vigencia gracias a la comunidad de Software Ágil, que ha tomado bastante de estos principios.

El fin de este post es enunciar los siete principios fundamentales de Lean Software Development y, con ellos, siete grandes mitos de la Ingeniería de Software que la filosofía Lean y la comunidad de metodologías ágiles intentan derribar. El material que presentaré a continuación fue extraído de una clase de Administración y Control de Proyectos Informáticos II dictada por Juan Gabardini en junio de 2008 en la FIUBA.

Principio 1: Eliminar el Desperdicio

Lean habla constantemente de desperdicio, llamándolo de una forma muy particular: "muda". Lean reconoce siete tipos de muda:
  • Actividades humanas que absorben recursos pero no crean valor
  • Errores que requieren rectificación
  • Ítems de producción que nadie quiere y que se acumulan en el inventario
  • Pasos de procesamiento que nadie necesita
  • Movimiento de empleados y bienes de un lugar a otro sin propósito
  • Capacidad ociosa (gente que espera ociosa a que otra actividad anterior a la suya que no entregó a tiempo finalice)
  • Bienes y servicios que no responden a la necesidad del usuario
En software se considera como desperdicio a cualquier artefacto que no genere valor al cliente, que no genere beneficio al cliente. El stock es desperdicio; los requerimientos, el diseño y las listas de bugs también lo son. Lean va más lejos y afirma que cualquier lista de administración (de bugs, de TO-DO, de requerimientos) es desperdicio. Esta afirmación es fuerte, pero es cierta teóricamente hablando, ya que cualquiera de estas listas insume tiempo de administración y de priorización y repetidas repriorizaciones que en definitiva no aportan valor al cliente. Son waste.

MITO: La especificación temprana reduce el desperdicio.

Este mito es derribado usando como pretexto que el negocio puede cambiar durante el desarrollo del producto. Todo lo que se definió en forma temprana puede cambiar al mes o a la semana siguiente. La mitad de los requerimientos pueden dejar de ser prioritarios, dejar de aportar valor al cliente. Por eso los sprints de Scrum suelen ser de dos semanas a un mes, y no más largos.

En Lean, la funcionalidad no usada es considerada como la mayor fuente de desperdicio porque produce costo en toda la cadena de desarrollo y no genera valor. Hay una frase muy famosa en nuestro ámbito que dice: "el 80% de los usuarios usa el 20% de la funcionalidad". La funcionalidad no usada no sólo insume costos en el desarrollo del feature en sí, también es muda porque hace más grande el código, por ende se hace más complejo, más propenso a errores y todo esto implica mayor arquitectura, mantenimiento, refactoring, etc.

Principio 2: Construir con Calidad

¿Es bueno testear y encontrar defectos en un producto? Por supuesto es mejor que el equipo de desarrollo encuentre los defectos a que los encuentre el cliente. Pero la verdad es que mucho mejor sería que no los haya. ¿Por qué no construir bien desde la primera vez?

Siguiendo con la filosofía del TPS, Lean propone que cuando se encuentren defectos, hay que parar la línea de producción para corregir la causa raíz y que este defecto no vuelva a ocurrir. Se debe atacar los bugs en cuanto se detecten. Recuerden que una lista de bugs enorme insume mucho tiempo de priorización y administración; es desperdicio.

MITO: El trabajo del tester es encontrar defectos.

Un tester debe incorporar la calidad en el desarrollo lo antes posible. El tester debe ayudar a que cuando se produzca un problema se encuentren las causas raíces y se arreglen. Lean tiene una visión proactiva de la calidad. Medir la calidad al final es la peor forma de corregir problemas (por supuesto es mejor que no medirla). Calidad no es Testing. Testing es una forma de medir la Calidad.

¿Cómo se lleva esto a la práctica? El tester debe ayudar a que el programador haga buenas pruebas unitarias y debe armar las pruebas de aceptación correctamente, y en lo posible automatizar toda prueba para saber, de forma rápida y efectiva, que el producto cumple con los requerimientos del usuario en todo momento. El rol del tester pasaría a ser QA, pero no QA de Quality Assurance, QA de Quality Assistant. La forma que QA tiene ahora de asegurar la calidad no es testeando y reportando bugs, sino ayudando en todo el proceso de desarrollo. Ayudando también al Product Owner a expresar sus requerimientos de forma testeable, al diseñador para que haga una arquitectura y diseño testeables, etc.

Principio 3: Crear Conocimiento

Lean ve al desarrollo de un producto como una oportunidad única de aprendizaje y mejora. Una empresa que aprende, que crea conocimiento y que lo difunde entre todos sus equipos de trabajo, es una empresa evolutiva, abierta a la mejora continua y al crecimiento.

MITO: Las predicciones crean predictibilidad.

Esto significa que si yo estimo de una manera correcta y analizo el problema, entonces voy a tener una estimación que me va a ayudar a cumplir con las fechas propuestas. Mis predicciones de cómo va a ser el futuro aumentan la probabilidad de que ocurra lo que yo predije.

Dicho así parece obvio que no podemos modificar el futuro simplemente con decirlo. Hay que aceptar que el mundo es variable, que siempre hay cambios que no voy a poder incluir en mis predicciones. Crear conocimiento está más orientado a mejorar mi forma de trabajo que a predecir manteniendo mi forma de trabajo actual.

Cuando uno estima en un entorno ágil, nunca lo hace a largo plazo. Por eso los sprints de Scrum son tan cortos, porque cuanto más cercano es el futuro que intento predecir, menos probabilidad existe de equivocación. Es como si apuntara con una escopeta; si estoy demasiado lejos del blanco, cualquier mínima variación hacia donde apunto producirá un desvío brutal en la variación de la trayectoria.

Principio 4: Postergar el Compromiso

Este principio dice que hay que tomar las decisiones lo más tarde posible (ni más tarde, ni menos). Es el mismo concepto de ALAP en Critical Chain. De esta forma uno genera decisiones más comprometidas y siempre trata de buscar soluciones que sean reversibles. Tomar las decisiones lo más tarde posible implica más conocimiento del negocio, mayor certidumbre y por ende menor retrabajo.

MITO: Planificación es compromiso

La planificación implica contar con una serie de datos muy detallados en etapas muy tempranas del proyecto. El cono de incertidumbre en esto es implacable. Todas las partes involucradas tienen que entender que la planificación es un rango de probabilidades. Se puede achicar los rangos con iteraciones más chicas, pero definitivamente la predicción del futuro no significa compromiso, sino no seríamos informáticos sino futurólogos.

Como en el mundo laboral existen compromisos que hay que tomar, la comunidad ágil propone distintas formas de contrato a las convencionales. Por ejemplo, puede haber una cláusula que diga que si una entrega no agrega valor al cliente se cancela. Esto es bueno ya que a la hora de hacer el contrato ni una parte ni otra sabe a ciencia cierta qué puede dar valor dentro de unos meses. Otra forma de contrato ágil podría ser que si para el cliente el producto, en un momento dado, tiene suficiente calidad y funcionalidad para ser pasado a producción en forma definitiva y se decide cancelar el proyecto, cliente y proveedor se reparten la mitad del beneficio que queda.

Principio 5: Entregas Rápidas

Las entregas rápidas implican alta calidad, por ende bajos costos. Si las pruebas están automatizadas, los costos de setup van a ser menores, por ende se logrará reducir el costo de cada liberación, entregar más rápido y reducir el retrabajo. También es un concepto heredado de Toyota. En Toyota se trabaja JIT, sin stock. La demanda rige despóticamente sobre toda la línea de producción. Nada se construye de más. Cuando hay una orden de pedido, se construye sólo esa orden de la forma más rápida y efectiva posible y se entrega cuanto antes. Así fue como los japoneses de Toyota lograron hacer tambalear la posición de líder de mercado que hasta entonces tenían los americanos con Ford y sus entregas lentas y poco personalizadas.

MITO: El apuro causa desperdicio.

"Ágil es rápido, no apurado". El apuro sí causa desperdicio, la rapidez no. Si se trabaja rápido, menos y de forma efectiva, y rigurosamente día a día se hace refactoring y se mejora las condiciones de trabajo, las cosas se pueden hacer bien la primera vez.

Principio 6: Respetar a las Personas

Lean pone especial foco en el respeto a las personas. Las personas no son robots, ni engranajes, ni operarios bobos (tipo Chaplin en "Tiempos Modernos") que siguen las especificaciones escritas por un consultor al pie de la letra sin pensar. Se tiene absoluta confianza en el equipo de trabajo. Cuando hay un problema, no se culpa a la persona, sino al sistema que permitió a la persona cometer el error.

Para lograr entregas rápidas, mejora continua y confiar plenamente en las personas, se necesita equipos con buen skill, con alta experiencia técnica, que es uno de los requerimientos más criticados en los entornos ágiles. No puedo pedirle a un programador junior que recién está aprendiendo Java, que codifique rápido, bien y encima que se enfoque en las meta-tareas de generar conocimiento para la empresa, refactoring y mejora continua.

También es muy importante contar con líderes emprendedores, proactivos y verdaderos coach.

MITO: Existe la mejor manera de hacerlo.

Este mito asume que un consultor, por ejemplo, sabe más que las mismas personas que hacen su trabajo, y que debe entrenarlos para que sigan el estándar que él mismo definió. Viene de la vieja escuela que dice: "Ustedes tienen que hacer las cosas así", donde un jefe que estima y asigna tareas a las personas, genera compromiso obligado a sus empleados para que cumplan sus predicciones. "Yo, consultor especializado, te voy a decir a vos cómo es la mejor forma de hacer tu trabajo". Esto, en cualquier entorno ágil o de mejora continua, no existe.

Principio 7: Optimizar el Todo

Optimizar el conjunto. No caer en optimizaciones locales. Por ejemplo, no se puede optimizar el sector de ventas sin optimizar el sector de producción, o de entregas, porque de nada sirve vender para ayer algo que todavía no está hecho ni se puede hacer de una forma rápida. Otro ejemplo podría ser el de un equipo de Testing saturado. La solución más local sería incorporar más testers al equipo, pero esto podría no solucionar el problema, ya que la causa raíz podría provenir de otro equipo, de que desarrollo le está entregando funcionalidad demasiado tarde o Testing no cuenta con las herramientas necesarias para hacer su trabajo.

MITO: Optimizar por descomposición.

Este mito dice que siempre que divida venceré. Un criterio que los programadores conocemos muy bien. Pero esto no siempre es cierto para el mundo de las empresas. Por ejemplo, yo puedo dividir hasta el límite el sector de Compras y contratar al proveedor más óptimo para cada materia prima. Es muy probable que esto no funcione correctamente, ya que no es bueno tener tantos proveedores. Con cada tercerización se necesita una interface clara y trabajada, se necesita confianza, trato. El tiempo y los riesgos de contar con un proveedor, por más bueno que sea, se multiplica por la cantidad de proveedores que tenga. En Critical Chain cada vez que se depende de algún módulo de tercero, se agrega un buffer de tiempo, porque esta interdependencia entre empresas se considera un riesgo alto.

viernes, 11 de julio de 2008

Team Skill 4: Administrando el Alcance

Es hora de seguir con los post dedicados a Managing Software Requirements - A Unified Approach de Dean Leffingwell y Don Widrig (ISBN: 0-201-61593-2). Ya conté que este libro data de 1999 (podría decirse que es un veterano en lo que respecta a Ingeniería de Software) y presenta una metodología ordenada que puede servir como guía a la hora de identificar, analizar y especificar formalmente los requerimientos de un sistema de software y que ayuda en la administración de cambios de los mismos. Para presentar los temas de una forma ordenada, Leffingwell y Widrig han dividido esta metodología en 6 Habilidades Grupales (Team Skills). En los post anteriores, ya vimos tres de estas habilidades:
  • Team Skill 1: Analizando el Problema: que básicamente se encarga de las disciplinas de Business Modeling y de entender cuál es el problema del usuario, mediante cinco pasos concretos que pueden ayudar a identificar las causas del problema, los stakeholders, las restricciones, etc.
  • Team Skill 2: Entendiendo las Necesidades del Usuario: que básicamente se encarga de suavizar tres grandes síndromes que suelen ocurrir a la hora de capturar requerimientos, mediante distintas técnicas de relevamiento propuestas.
  • Team Skill 3: Definiendo el Sistema: que habla un poco sobre cómo mediante documentos de Visión, de Especificación de Software y tal vez de Marketing, definir formalmente el Alcance del Sistema.

Lo que vamos a ver hoy, en la Habilidad Grupal que nos ocupa, es la forma que Managing Software Requirements propone para administrar el alcance definido en los Team Skills anteriores. El problema del alcance del proyecto es una dificultad común. No es inusual que los proyectos sean iniciados con el doble de funcionalidades que un equipo puede implementar con una calidad aceptable. La causa principal: los clientes quieren más, marketing quiere más y el equipo quiere más. Pero lamentablemente, cuando los tiempos apremian, debemos asegurarnos de poder entregar algo a tiempo.

Estableciendo el Baseline del proyecto

Con el fin de administrar el alcance, el libro presenta la noción de baseline, término muy usado en el mundo de la administración de proyectos. El baseline es un acuerdo para la comprensión de lo que el sistema va a hacer, un conjunto de ítems de configuración compuestos por funcionalidades o requerimientos dispuestos a ser entregados en una versión especifica de la aplicación. Si el alcance y las expectativas exceden la realidad, probablemente el proyecto esté destinado al fracaso. Después de todo, nosotros somos sólo los recursos, no los que tomamos las decisiones finales. Por lo tanto, la pregunta es: ¿Qué debe estar completado en la siguiente liberación (release), dados los recursos que estarán disponibles para el proyecto?

Como Dean Leffingwell y Don Widrig son de verdad muy técnicos y meticulosos, para llevar a cabo la definición del baseline proponen 5 pasos que pueden sernos de gran ayuda:
  • Paso 1: Armar una lista numerada de funcionalidades.
  • Paso 2: Priorizar las funcionalidades de la lista: Se puede establecer un ranking de 3 valores, por ejemplo: Crítica, Importante y Útil. Obviamente uno puede elegir la categorización que más le satisfaga, pero siempre hay que tener en cuenta que cuanto más valores tengamos, la lista será más difícil de mantener y de utilizar. (Imaginen si usáramos una escala del 1 al 10.)
  • Paso 3: Estimar el esfuerzo: Lo mismo que en el paso anterior. Como ni siquiera contamos con una WBS bien definida, seguramente las funcionalidades no estarán explotadas al nivel de detalle necesario para poder estimar con el detalle de horas. Por lo tanto, también se sugiere una escala de 3 valores como: Alto, Medio y Bajo.
  • Paso 4: Estimar el Riesgo: Lo mismo que antes: Alto, Medio y Bajo.
  • Paso 5: (si es necesario) Reducir el Alcance: Conviene que el baseline quede establecido con requerimientos críticos y uno o más importantes.

Negociando con el Cliente

Para establecer el baseline indudablemente vamos a tener que llevar a cabo algunas negociaciones. El libro menciona algunas buenas recomendaciones que el equipo puede necesitar en algunas ocasiones:
  • Empezar alto, pero no irrazonable.
  • Separar a las personas del problema.
  • Enfocarse en intereses, no posiciones.
  • Entender la posición de cierre.
  • Inventar opciones para mutuo beneficio.
  • Aplicar criterios objetivos.
  • Sub-prometer y sobre-entregar.
Leffingwell y Widrig no esperan que el proceso describa una forma en la que el desafío de definir el alcance desaparezca. Sin embargo, los pasos esbozados pueden esperar a tener un efecto material sobre el alcance del problema, permitiendo a los desarrolladores enfocarse en subconjuntos críticos e incrementar la calidad del sistema para que cumplan o superen las expectativas del usuario. Además, la participación del cliente en ayudar a resolver el problema de la administración del alcance aumenta el compromiso de ambas partes y fomenta una mejor comunicación y confianza entre el cliente y el equipo de desarrollo de la aplicación.

Con una clara definición del proyecto, o un documento de Visión en la mano, y el alcance logrado a un nivel razonable, contamos con más herramientas para encarar las siguientes fases del proyecto. Lo que sigue, el Team Skill 5: Refinando la Definición del Sistema, será en el contexto de la fase de Elaboración de un proyecto, donde toma rol protagónico la disciplina de Análisis & Diseño, continúa por supuesto la toma de requerimientos y el modelado del negocio y se comienza con la implementación del sistema.

Entradas Relacionadas

martes, 8 de julio de 2008

Dificultades Comunes al Implementar RUP


Esto lo saqué de una presentación oficial de IBM que habla sobre "El espíritu de RUP". Hay unas slides, casi al final, que enumeran las dificultades comunes con las que un equipo de desarrollo que implementa RUP suele enfrentarse. En negrita, para cada una de las fases del ciclo de vida del proyecto, la dificultad/error/problema que es frecuente en los equipos que implementan RUP. Como sub-ítems de esos problemas, las soluciones que RUP propone, lo que RUP dice al respecto o, en algunos casos, preguntas cuyas respuestas pueden llevar a la explicación de por qué puede estar sucediendo esto.

Dificultades Comunes en Inception

Demasiada formalidad / Demasiados artefactos
  • Sólo producir los artefactos que agreguen valor.
  • Minimizar la formalidad si es posible.
  • Cuando es dudoso el valor que aporta algún artefacto, no realizarlo.
La Parálisis del Análisis
  • Los artefactos se pueden mejorar más tarde.
  • Es importante avanzar, no estancarse en el análisis.
  • Mantener foco en los objetivos de la fase de Inception.
  • No describir todos los requerimientos en detalle.
Iteración Inicial muy Larga
  • Recortar el alcance rápidamente.
  • Si la primera iteración falla, el proyecto entero estará destinado a fallar.

Dificultades Comunes en Elaboration

Dejar la "Parte Difícil" para Después
  • Atacar los riesgos en forma temprana, o los riesgos lo atacarán a usted.
  • Atacar la parte difícil ahora, para tener una vida más fácil después.
No Implementar ni Validar la Arquitectura
  • No se puede obtener una arquitectura correcta o abordar los riesgos principales sin implementar y testear la arquitectura.
  • Construir una Arquitectura Ejecutable y Testearla.
Los Cambios No Son Bienvenidos
  • Los cambios permiten mejorar.

Dificultades Comunes en Construction

Basar el Trabajo en una Arquitectura Inestable
  • Mayor retrabajo y bugs de integración.
  • El precio a pagar por un trabajo insuficiente en la fase de Elaboration es alto.
Reinventar Soluciones a Problemas Comunes
  • ¿Fueron desarrollados los patrones de arquitectura en la fase de Elaboration y comunicados a todo el equipo?
No se realiza Integración Continua
  • Un build diario o semanal minimiza el retrabajo.
Las Pruebas no se Inician hasta el Final de la Construcción
  • Es muy probable que no lleguemos a los deadlines.
  • La versión beta puede resultar de tan baja calidad que ni siquiera ofrezca valor.

Dificultades Comunes en Transition

No Toda la Funcionalidad fue Testeada
  • ¿Se incluyó funcionalidad no testeada en la release?
  • ¿La release es utilizable (Fácil de usar, performante, documentada, ...)?
El Cliente no está Contento con la Funcionalidad Entregada
  • ¿Los clientes aprobaron los criterios de aceptación?
  • ¿Estuvo involucrado el cliente en el desarrollo del proyecto?

El Espíritu de RUP

En base a estos problemas frecuentes, Rational Unified Process presenta ocho fundamentos clave, que son el alma o el espíritu de RUP:
  1. Ataque los riesgos mayores en forma temprana y continua... o ellos lo atacarán a usted!
  2. Asegúrese de entregar valor al cliente
  3. Mantenga un foco obsesivo en la construcción del software
  4. Asimile en forma temprana los cambios en el proyecto
  5. Construya una arquitectura ejecutable lo antes posible
  6. Construya el sistema con componentes reutilizables
  7. Asegúrese de que todo el equipo trabaje unido
  8. Haga de la calidad una forma de vida y no una disciplina de último momento

martes, 1 de julio de 2008

Lenguajes Funcionales

Existen cuatro grandes paradigmas entre los lenguajes de programación:
  • Lenguajes Imperativos: Pascal, C, Fortran.
  • Lenguajes Orientados a Objetos: Smalltalk, Java (no puro), Python, Ruby.
  • Lenguajes Lógicos: Prolog.
  • Lenguajes Funcionales: FP, APL, Lisp.
Los lenguajes funcionales suelen usarse mucho para Inteligencia Artificial. Para alguien que está acostumbrado a programar en lenguajes imperativos u orientado a objetos, la curva de aprendizaje es un poco más grande; cuesta un poco entender el paradigma. Lo más chocante es que en general no existe la asignación de variables, se trabaja en forma recursiva, con memoria dinámica, y, en algunos lenguajes, cualquier programa se puede escribir en una sola línea (por ejemplo en APL), lo que dificulta un poco su lectura.

http://en.wikipedia.org/wiki/Functional_programming

Cálculo Lambda

Cálculo Lambda es una base teórica matemática para definir lenguajes funcionales. Data de la década de 1930 y fue creado por Alonzo Church y Stephen Cole Kleene, tres décadas antes de que existieran las computadoras.

http://en.wikipedia.org/wiki/Lambda_calculus

FP (Functional Programming)

Este lenguaje nace como un lenguaje teórico del paper de John Backus de 1977 titulado: "Can Programming be Liberated from the von Neumann Style?".

Hay algunos intérpretes libres circulando por Internet, aunque no existe ninguna implementación comercial. Las que hay son versiones hechas por universitarios.

FP maneja básicamente dos tipos de datos:
  • Átomos: pueden ser números, strings, valores lógicos (True y False) o el elemento vacío. Ejemplos: a, b, 1, 2 0.
  • Secuencias: son como arrays o listas y se escriben con corchetes angulares. Ejemplos: <a b c> <d e> <<d e> <a b>> < > (La secuencia vacía es equivalente al elemento vacío y es átomo y secuencia a la vez).
El lenguaje está compuesto por los tipos de datos y las funciones, que pueden ser:
  • Primitivas
  • Formas Funcionales
  • Definidas por el Usuario
Las funciones no tienen argumentos. Son monádicas. Lo único que reciben como entrada es el ambiente.

FP es un lenguaje muy compacto y poderoso, a pesar de no contar con implementación comercial. Hay tres distintas formas de trabajar con FP:

de modo Recursivo:


de modo Funcional:


de modo Iterativo:


El código que mostré para cada modo es el de la función factorial. Comparando las tres funciones, vemos que la del modo funcional es la más compacta y elegante. Esto sucede así, ya que es el modo que mejor soporta el lenguaje; el más cercano al paradigma. El tercer modo, el iterativo, es el más similar a un lenguaje imperativo.

http://en.wikipedia.org/wiki/FP_programming_language

APL (A Programming Language)

Creado en 1957 por Kenneth E. Iverson. Fue hecho para resolver problemas matemáticos. Entre otras aplicaciones, se usó para describir el hardware de la IBM 360, para el sistema Deep Blue de IBM que venció a Kasparov al ajedréz y para generar los efectos de la célebre película Tron de Walt Disney.

Existen varias implementaciones comerciales y libres de APL. Algunas de ellas son:

Free APL
APL 2000
APL 2C

(Particularmente, sólo he probado la Trial Version del APL 2C para Windows y la verdad que me pareció muy cómoda y user friendly, ya que uno puede seleccionar los caracteres extraños haciendo click con el mouse en un tecladito que se dibuja debajo de la ventana. En cambio, en los otros intérpretes uno tiene que memorizar una combinación de teclas para nada amigable.)

APL es el lenguaje más compacto que alguna vez vi. Todo, absolutamente todo, puede escribirse en una sola línea. No se usa declaración de identificadores, ya que nada está declarado estáticamente. Las asociaciones son dinámicas, por ende cualquier variable se puede asociar con cualquier tipo de dato.

Los tipos de datos pueden ser:

Simples: números, booleanos, strings.
Estructurados: arreglos multidimensionales homogéneos.

Todo el lenguaje está compuesto por los tipos de datos y un conjunto de operadores, que pueden ser monádicos o diádicos.

Escribir un programa que calcule el factorial en este lenguaje es trivial, ya que tenemos el operador ! que ya hace eso.

APL es excelente para trabajar con matrices porque cuenta con operadores de trasposición, inversión y de espejamiento de matrices.

El siguiente código de ejemplo devuelve todos los números primos comprendidos entre 1 y N:


Un tema curioso es que la ejecución del código se hace de derecha a izquierda y el orden de ejecución de los operadores es en ese orden también.

http://en.wikipedia.org/wiki/APL_programming_language

LISP (List Processing)

Es el lenguaje más popular del paradigma funcional.

Fue creado por John McCarthy en 1958.

En general, los intérpretes de Lisp están escritos en C y existen varias implementaciones. Algunas de ellas son:

XLISP
XLISP-PLUS
CLISP
CUSP (un plugin para Eclipse)

(He probado el XLISP-PLUS, el CLISP, el TLC-LISP -que no menciono en la lista porque no encuentro una página de donde bajarlo- y el plugin para Eclipse. A pesar de que tiene bastantes bugs, el que más me simpatizó es el plugin para Eclipse de IBM. Unos amigos que tienen Windows me dijeron que no lo pudieron instalar. Yo tengo Ubuntu Hardy Heron y no tuve problemas. Si uno quiere algo más liviano y menos bugeado, CLISP y XLISP o XLISP-PLUS son las mejores opciones.)

Los tipos de datos (objetos) que forman el lenguaje son:

Átomos: 2, 3.2, a, CASA, +, &, T, nil
Listas: ((a b) c d) (a b) ((a b) (c d)) ()

Los valores lógicos son T (true) y nil (vacío, que equivale a false). Además, () equivale a nil. Al igual que en FP, la lista vacía es átomo y lista a la vez.

Todo en Lisp es una lista o un átomo. Aunque parezca extraño, también una llamada a función. Por ejemplo:

(+ 3 2)

Como FP, Lisp cuenta con funciones nativas (una larga lista: selectoras, constructoras, reconocedoras de objetos, aritméticas, booleanas, relacionales, condicionales, funcionales -similares a las formas funcionales de FP-, etc), funciones definidas por el programador (con nombre o anónimas) y además abundan vastas bibliotecas de terceros que complementan muy bien al lenguaje.

Lisp es el más poderoso de todos. Es muy cómodo para programar secuencias de tareas parametrizadas. Por eso suele usarse para la programación de robots o para la creación de algún intérprete (muchos de los intérpretes universitarios de FP que andan dando vueltas están hechos en LISP).

Lo más extraño que tiene este lenguaje es que todo, absolutamente todo, se ejecuta, a menos que se especifique lo contrario con la función QUOTE.

Por ejemplo, si uno escribe:

(car (1 2 3))

CAR es un selector que recibe una lista por parámetro y devuelve la cabeza. Si se hubiera escrito esta línea, así como está, se hubiera devuelto un error, ya que el intérprete no podría encontrar la función "1" (a menos que la función "1" haya sido definida con anterioridad).

En cambio, si escribimos:

(car (quote (1 2 3)))

O simplemente:

(car '(1 2 3))

El intérprete devuelve 1, como debería ser.

La función factorial escrita en Lisp (con el dialecto Common Lisp) sería:

(defun FACT (N)
(if (eq N 0) 1 (* N (FACT (- N 1))))
)

Lisp es altamente recursivo.

http://en.wikipedia.org/wiki/Lisp_programming_language

Conclusiones

Los lenguajes funcionales no son populares salvo en ambientes universitarios. Se suelen usar mucho para investigación y, en general, resultan herramientas muy poderosas para cálculos matemáticos, definiciones y demostraciones.

Algunos lenguajes del mundo de la orientación a objetos se están dando cuenta de su poder y, de a poco, están incluyendo bibliotecas para trabajar de forma funcional. Uno de los casos más conocidos es el del monstruo Java, cuya comunidad se debate si los closures (objetos funcionales del mundo del paradigma funcional) deben formar parte de la especificación estándar del JDK de Java 7.