Feeds:
Entradas
Comentarios

Archive for 29 noviembre 2010


Como el post anterior sobre contratos cerrados me quedó un poco abstracto, en este voy a poner un ejemplo, con el que espero ser capaz de clarificar mi idea. Suponed que existen dos versiones de vosotros en dos mundos paralelos. En un mundo, el mundo verde, usáis scrum, y tratais de entregar funcionalidad real al final de cada sprint. En el otro mundo, el mundo rojo, usáis una metodología iterativa tradicional, donde al final de cada iteración entregais artefactos correspondientes a entregables de la cascada (diseño, análisis, capa de persistencia, etc). En ambos mundos el cliente os contrata para hacer el mismo proyecto, bajo contrato cerrado y con las mismas condiciones.

La siguiente figura ilustra lo que ocurre si aprovechando vuestras capacidades CMMI5, conseguís hacer una planificación perfecta durante la preventa y terminais el proyecto en tiempo, dinero y alcance según lo planificado en el contrato cerrado. Podéis observar que aunque en ambos proyectos se ha tenido un éxito rotundo, en el mundo verde se ha ido entregando funcionalidad desde antes. Pero, como el proyecto ha sido exitoso eso sólo son detalles que le importan a los frikies.

Entrega continua de funcionalidad al usuario

Proyecto exitoso (un mundo perfecto)

En el gráfico anterior, se ha medido la entrega de funcionalidad con respecto al paso del tiempo. Sin embargo no todas las funcionalidades son igual de importantes para el cliente. A la importancia de una funcionalidad en concreto lo llamamos valor. En scrum la funcionalidad se entrega por orden de ROI. El ROI es el balance entre el valor de una funcionalidad y su coste estimado. Cuanto más valor más ROI; cuanto menos coste más ROI. Sin embargo, en la práctica, las diferentes funcionalidades se van analizando, refinando y subdividiendo, hasta que éstas tienen un tamaño tal que nos permite, de forma razonable, implementarlas en un único sprint. Debido a esto las funcionalidades que se entregan al final de cada sprint tienen un tamaño similar. En este sentido, y de forma grosera, podemos tratar el factor coste dentro del ROI como una constante. Por lo tanto, de forma aproximada, Scrum entrega las funcionalidades con mayor valor primero, maximizando así el flujo de entrega de valor. Sin embargo en el mundo rojo la funcionalidad se entrega en cuanto se puede. El orden de entrega lo define, no el valor, sino el orden en el que se planificó (cascada) la entrega de todos los artefactos que componen una funcionalidad dada. En este sentido el mundo rojo entrega funcionalidad sin atender al valor, con lo cual el valor de cada entrega será “aleatorio”. Si en vez de medir funcionalidad entregada, medimos valor o utilidad entregada, el gráfico anterior se transforma en el siguiente:

Entrega de valor al usuario

Valor entregado en un proyecto exitoso (un mundo perfecto)

De nuevo ambos proyectos tienen éxito, pero en el segundo gráfico se detecta una diferencia aun mayor de entrega de valor entre los dos mundos. De nuevo esto es una curiosidad frikie.

Sin embargo podría haber ocurrido otra cosa. Ciertamente usando CMMI5 y nuestros mejores algoritmos de planificación y estimación, nuestro plan para el contrato cerrado es impecable y perfecto. Sin embargo el mundo es muy cruel, y el rival de nuestro cliente se adelanta y saca antes que nosotros un producto que es directa competencia del nuestro. El cliente desesperado decide salir a producción cuanto antes, con lo que haya, para minimizar perdidas debido a un “time to market” tardío. Lo siento chic@s, el negocio manda. Supongamos que esto ocurre en la iteración 7, veamos el gráfico.

Entrega de valor si entramos en producción inesperadamente

La competencia se nos adelantó (esto es más realista)

¡ Oh, qué pena ! Si la competencia hubiera tardado una iteración más, el mundo rojo hubiera podido entregar un 65% de valor. Desgraciadamente los muy desalmados decidieron sacar el producto a mitad de la iteración 7 y pilló al equipo del mundo rojo integrando la capa de persistencia con el gestor transaccional y haciendo el “documento de diseño de algoritmos concurrentes” de entrega obligatoria en esa fase de la cascada. Sólo pudieron entregar un 10%. En el mundo verde también hemos fracasado, pero al menos conseguimos entregar un 70% de funcionalidad. Desgraciadamente el “documento de diseño de algoritmos concurrentes” está en blanco y no tiene ni índice, que pena más grande, el señor auditor de calidad y responsable de metodología nos va a reñir.

Olvidémonos ahora de la competencia, supongamos que todo lo anterior fue una pesadilla y que realmente tenemos todo el tiempo acordado para entrar en producción, al fin y al cabo, es un contrato cerrado, ¿no? Lo que nos ocurre ahora es que le mentimos al cliente, para ganar el proyecto tiramos los precios y nuestros recursos no son tanto como los que dijimos. O tal vez no, tal vez simplemente no comprendimos tan bien los requisitos como pensamos y estimamos mal. O quizás el tiempo y el precio nos venían impuestos y pensamos que eran asumibles, pero no lo pensamos mucho. Estos escenarios, donde la planificación y estimación inicial no se corresponden con la realidad es el caso más común. Desafortunadamente el contrato cerrado nos impide renegociar tiempo y dinero y por eso constituyen un riesgo tanto para el cliente como para nosotros.

El siguiente gráfico ilustra una situación parecida:

Entregas de valor en proyecto cerrado con mala estimación

Nos hemos colado en la planificación (como siempre)

De nuevo scrum se centra en maximizar el flujo de valor entregado, con lo que conseguimos en este ejemplo un 60% de valor. El mundo rojo no se centra en el valor sino en artefactos con lo que el valor entregado es sólo el 35%. La hemos pifiado en los dos mundos, pero de nuevo insisto, scrum falla mejor.

A qué mundo preferís pertenecer, ¿al verde o al rojo? Espero haber dejado clara mi postura. Por supuesto los gráficos son ficticios.

Pero no cantéis victoria. Scrum sólo os dice que tenéis que entregar valor y funcionalidad al final de cada sprint, pero no cómo. El cómo conseguir esto es algo muy duro y complicado, y Scrum no te ayuda en nada en este aspecto. Necesitareis un equipo con talento y que sepa construir software de manera ágil. Esto parece material para otro post.

Read Full Post »


Hola a todos, aquí vuelvo a la carga con un nuevo post para desgracia de mis minoritarios lectores. El tema de los proyectos cerrados ya lo traté en otro post, explicando que en realidad el pensar que un proyecto está cerrado en alcance, tiempo y recursos es algo totalmente utópico. Sin embargo debido al ambiente de desconfianza que existe en nuestro sector entre clientes y proveedores, surgió esa mala bestia, el contrato cerrado, causante de tantos entuertos en nuestra profesión. Dado que ejercemos nuestra profesión en un ambiente hostil, nueve de cada diez veces vamos a encontrarnos en un proyecto con contrato cerrado. Los proyectos con contratos cerrados se han puesto siempre como ejemplo prototípico de un entorno donde Scrum y otras metodologías ágiles no podrían funcionar. Como este tipo de proyectos son los más comunes, muchos han usado esta aseveración para defender la postura de que trabajar en modo ágil es imposible en España (y alrededores). Yo no estoy para nada de acuerdo con esto, la verdad es que lo considero totalmente desacertado y un síntoma de lo mal que se está entendiendo el movimiento ágil. De hecho mi postura es radicalmente opuesta: los proyectos cerrados constituyen nuestro mejor aliado para implantar el modelo ágil en la empresa. O sea, no solo no pienso que el proyecto cerrado impida la gestión ágil, sino que es un aliado a la hora de hacer ver a nuestro entorno lo positivo que es Scrum. Todo pasa por una pequeña revelación: scrum es más eficiente que la cascada, sobre todo en proyectos cerrados. Aunque ya comenté algo al respecto en mi post anterior, quiero dar más detalle sobre mi postura a este respecto. Lo veremos a lo largo de este post, pero no os preocupéis, si se hace muy largo pienso dividirlo.

Como sabréis Scrum es un proceso iterativo, que al igual que cualquier otra metodología iterativa, divide la gestión y control del proyecto en pequeñas fases o iteraciones (sprints en scrum). Al final de cada pequeña fase se inspecciona el progreso del proyecto y se ajustan las planificaciones y la estrategia de desarrollo. El modelo clásico de cascada no es iterativo y se basa en una planificación y diseño detallado, tras la cual se realiza la construcción, y el producto no es inspeccionado y revisado por el cliente hasta el final. Obviamente el realizar la planificación y diseño detallado al principio es un error tremendo, ya que es cuando menos información tenemos, los requisitos están menos claros y la incertidumbre es mayor, con lo que seguro que nos vamos a equivocar en la planificación, y por mucho.

Es obvio, que una metodología iterativa es más eficiente que la cascada. En el caso de un contrato cerrado nadie nos libra de un análisis de requisitos con cierto detalle y una estimación afinada en la fase de preventa. Esto es intrínseco al modelo de contrato cerrado y concurso, necesitamos saber si podemos ir o no a presentarnos al proyecto y por cuánto dinero. Ya dije que esto es lo peor que podemos hacer, entonces, ¿de qué nos valen las iteraciones en un contrato cerrado? Simplemente para tener feedback frecuente. Esto redunda en un mayor control, por ejemplo:

  • Si el proyecto va muy mal, se puede detectar pronto, y cancelarlo con el menor coste para ambas partes. Algunos clientes (no todos) os agradecerán esta sinceridad e interés por no hacerle perder más dinero y tiempo.
  • Si el proyecto va muy mal, no se puede cancelar pero se puede retrasar, tal vez podáis negociar con el cliente un plazo extra de tiempo. Sí, sí, es contrato cerrado, pero el proyecto real no, en la práctica si el time-to-market no es muy crítico se puede negociar un retraso (aunque no te lo paguen).
  • Si el proyecto va muy mal, pero ni se puede cancelar ni retrasar, tendréis que usar la técnica de “más madera”. Si el cliente es bondadoso, podéis pactar un coste extra, aunque sea un contrato cerrado.
  • Si el proyecto va mal, pero se puede salvar, tomar las medidas correctoras pertinentes.
  • Si alguna circunstancia o requisito cambia os podéis enterar y aceptar el cambio de forma controlada. Sí, el contrato es cerrado, pero si el cliente decide cambiar un requisito o simplemente hubo un malentendido, lo correcto es aceptar el cambio de forma negociada. En caso contrario correis el riesgo de acabar el proyecto con éxito pero que no haga lo que quiere el cliente, y perder a éste de cara a futuros proyectos. Además, también las circunstancias y contexto del negocio cambia y no se pueden controlar, sería absurdo ponernos la venda en los ojos y pensar lo contrario.

O sea, que al planificar y cerrar la cosas al principio (contrato cerrado) estamos condenados a fallar. Pero usando una metodología iterativa podemos fallar por menos al controlar mejor el proyecto, e incluso aceptar cambios en el alcance si fuera necesario (aunque el contrato sea cerrado). También evita que andemos a ciegas hacia nuestra perdición. Estas ventajas están claras incluso en clientes muy tradicionales. La mayoría de ellos ya usan esquemas de trabajo iterativos, ya sea de forma explícita o implícita. En el último caso se suele tener un plan waterfall, pero muchas reuniones de seguimiento e hitos a intervalos regulares que implementan de facto un sistema iterativo. Otra variante se produce cuando el cliente divide un proyecto grande en múltiples fases, cada una de menos de tres meses, e intenta realizarlas de una a una en formato contrato cerrado. Desde el punto de vista del cliente esto es un ciclo de vida iterativo, desde el punto de vista del proveedor es simplemente una sucesión de concursos y contratos cerrados waterfall. Esta última forma de trabajar es bastante perversa, ya que no asegura al proveedor que realiza una fase del proyecto su participación en la siguiente, con lo que el incentivo por parte del proveedor para invertir en calidad disminuye, al no saber si se encargará de la evolución o mantenimiento prevista en la siguiente fase.

Como vemos el ciclo de vida iterativo es una mejora en proyectos cerrados, pero no es suficiente. Afortunadamente scrum no es sólo iterativo, sino incremental. En un proceso iterativo e incremental, al final de cada iteración no sólo se obtiene feedback de la marcha del proyecto, sino que se realiza una entrega parcial, que representa un incremento del grado de avance del proyecto. Existen varios procesos de desarrollo iterativos e incrementales, pero no todos ellos son ágiles. Los procesos iterativos e incrementales ágiles, como Scrum, entregan incrementos de funcionalidad, mientras que los demás entregan incrementos de construcción.

Un incremento de funcionalidad consiste en añadir nueva funcionalidad al sistema, que sea directamente útil al usuario, y que esté lista para entrar en producción, al final de cada iteración. Scrum sigue esta filosofía. El objetivo de cada sprint es entregar valor adicional al cliente, en forma de Product Backlog Items (PBIs) implementados y listos para entrar en producción. Por valor entendemos la utilidad, funcionalidad y/o rentabilidad de cada funcionalidad entregada. Los PBIs son unidades de funcionalidad, convenientemente analizadas y con una estimación de valor y coste. Pueden tener forma de historias de usuario o de casos de uso, según lo que se prefiera, pero eso no es lo importante. La estrategia de Scrum consiste no sólo en entregar valor al final de cada sprint, sino ir implementando los PBIs por prioridad, de forma que se entreguen primero los más importantes. La prioridad se calcula equilibrando el valor con el coste de cada PBI, de forma que dado el mismo coste se entreguen primero las de mayor valor.

La mayoría de las metodologías iterativas que se usan en el mundo empresarial no entregan incrementos de funcionalidad, sino incrementos de construcción. Un incremento de construcción consiste en entregar uno o más artefactos del proyecto al finalizar cada iteración. Ejemplo de tales artefactos son: documentos de diseño, esquema de BBDD, scripts de carga, capa de persistencia, pantallas. Estos artefactos, aunque necesarios para completar el proyecto, no aportan funcionalidad por si mismos. Estamos acostumbrados a que en cada hito, tengamos que entregar el documento de análisis, el de diseño, las tablas, etc. En el fondo esta forma de trabajar no es más que añadir un control iterativo a un ciclo de vida de cascada, de forma que cada iteración haga énfasis en una única fase de la cascada.

Otra diferencia importante entre los dos enfoques incrementales es en las reviews. En Scrum en cada sprint review se interactúa con el cliente, usuario y/o representante ya que vamos a enseñar funcionalidad. En esta reunión el cliente acepta o rechaza la nueva funcionalidad y nos proporciona un feedback de calidad sobre la funcionalidad del sistema. En el enfoque de entrega de incrementos de construcción no es posible interactuar con el usuario ni realizar aceptaciones de funcionalidad, ya que se entregan artefactos técnicos que son revisados por técnicos del cliente. En vez de aceptaciones de funcionalidad, se realizan aceptaciones de componentes tecnológicos. En este sentido no podemos tener feedback sobre la funcionalidad del sistema hasta bien tarde en el ciclo de vida de la aplicación (pruebas). Esto es totalmente ineficiente ya que el peor momento para detectar un error es al final, que es cuanto más caro es repararlo. También es bastante malo desde el punto de vista de la transparencia, ya que el usuario real no ve el sistema funcionando hasta el final. En realidad un enfoque iterativo incremental tradicional sigue siendo tan opaco para los usuarios como una cascada, ya que éste no puede interactuar con el sistema hasta el final. Esta forma de trabajar es un poco perversa, ya que al fin y al cabo, lo que el cliente conoce bien es la funcionalidad, y nos ha contratado porque sabemos de cosas técnicas. Lo lógico es que el cliente revise la funcionalidad, que es de lo que sabe y lo que le interesa, y que confíe en nuestro saber hacer. Tal vez esa confianza en nuestro saber hacer se ha visto envenenada por la moda de contratar “personal poco cualificado” y ofertar muy barato. Dicho todo esto, es obvio que Scrum proporciona una mayor transparencia y detecta los errores antes que una metodología iterativa incremental tradicional. Además proporciona resultados tangibles antes que un enfoque tradicional.

Como resumen, el hecho de aceptar un proyecto cerrado siempre conlleva un alto riesgo de no cumplir con las expectativas del cliente. Sin embargo scrum hace una mejor gestión que la cascada y otras metodologías no ágiles, al permitirnos:

  • Mitigar el riesgo de la estimación errónea. Si nos pasamos en la estimación al menos habremos entregado resultados tangibles y no artefactos “tecnológicos”.
  • Mitigar el riesgo de un lanzamiento prematuro debido a presiones de mercado. Si nuestros competidores lanzan su producto antes que nosotros, podemos ir a producción antes de lo esperado, ya que al menos tenemos algo con lo que ir a producción, en vez de documentos “técnicos”.
  • Ayudarnos a estimar mejor, al refinar mediante mejora continua nuestros procesos y usar timeboxing para que el proceso sea más predecible.
  • Ayudarnos a dar un precio más competitivo. De nuevo el timebox nos ayuda a enfocar nuestros esfuerzos y la mejora continua a aprender ser cada vez más productivos.
  • Mostrar al cliente el estado real del proyecto y entregarle valor de forma continua de forma que éste pueda empezar a confiar en nosotros. Tal vez el siguiente proyecto no sea tan cerrado…
  • Mitigar el riesgo/coste de cometer un error en el análisis funcional o aceptar un cambio. Detectar los errores cuanto antes es la forma más barata de solucionarlos, aceptar cambios de nuestros clientes es vital. Todo esto es posible ya que al final de cada sprint se puede mostrar funcionalidad, no tecnología, y por tanto recibir feedback del usuario.

En definitiva scrum falla mejor que otras metodologías a la hora de gestionar un proyecto cerrado. Los puntos claves que aporta scrum para conseguir todo esto son: transparencia con el cliente, mejora continua, iteraciones (sprints) y entrega incremental de funcionalidad real (valor).

Simplemente probad a gestionar vuestros proyectos cerrados con Scrum o similar, no es necesario que el cliente lo sepa de forma explícita. Seguro que la vais a pifiar (es un proyecto cerrado al fin y al cabo) pero lo haréis mejor que si hubierais usado la metodología “de toda la vida”, y vuestro cliente se sentirá mejor tratado y más satisfecho (pero no del todo). Construyendo pequeños éxitos, y poco a poco, vuestro cliente ganará confianza y podréis ir planteando otro tipo de relación, basada en la colaboración. Con el tiempo llegará el día que le podáis contar que usáis una cosa llama “agilismo”…

Ahora ya lo sabéis, no tenéis excusa, el agilismo va a llegar… incluso a los proyectos cerrados.

P.S. Uff, se ha terminado mi timebox de 2000 palabras, suficiente por hoy. En el siguiente post más sobre este tema, esta vez con dibujitos…

Read Full Post »


Hola a todos, lamento no haber podido escribir antes, pero tenía bastante trabajo acumulado y el deber me llamaba. Aprovecho que me iba a ir de fiesta esta noche y que al final no, para hacer algo útil con mi tiempo y escribir este post. Espero daros una crónica de lo que fue mi participación en la AOS2010, espero que un poco distinta y un pelín más crítica ;-)

Asistí junto con @ialcazar, @antoniodfr y @mcberros. Llegamos tarde, con la sesión de votación a mitad y un poco desubicados la verdad. Yo pensaba proponer una sesión llamada “los frameworks crean zombies” pero un tal Xavi Gost(@Xav1uzz) se me adelantó. Menos mal, porque con eso de llegar tarde, y ser mi primer open, estaba un poco cortado de verdad (a pesar del pequeño intento de @jmbeas de que saltara a la palestra). Mi compañero @antoniodfr no tenía muy claro si el formato open space iba a ser bueno o si por el contrario iba a degenerar en el caos. Al final se rindió al concepto de autoorganización y salió enamorado del concepto open.

Después vino una noche de parranda por los bares de Barcelona. Conté con la colaboración inestimable de Ramón Clariso y el inefable Aleix Cerecedo. Los bares al ver a una horda de agilistas por la noche de Barna iban cerrando a cal y canto, para que no entráramos en ellos ¡ y es que somos peligrosos ! Al final acabamos en un asador cerca del barrio de Graçia (¿está escrito bien?) donde tuvimos una animada charla con Roberto Canales(@rcanalesmora). Después el pobre Aleix tuvo la difícil tarea de buscar un bar en dicho barrio donde cupieran 40 personas. El pobre no dejaba de mirar atrás acojonado y diciéndome: “porque eres tu Enrique, sino salgo corriendo :-S “. Al final su plan “A” fue acertado y el sitio donde queríamos ir estaba vacío, pero en cuanto entramos quedo lo petamos. Desgraciadamente no servían mojitos, (mea culpa @kinisoftware) y lo peor es que no quedaba Moritz. Nos conformamos con gin-tonics y tercios de cerveza. A la llegada al hotel puse el despertador, este me informo amablemente que sonaría en sólo 3 horas, y cuando me quise dar cuenta ya estaba desayunando en el campus La Salle. Enrique, al lío.

Vayamos a las sesiones a las que asistí:

  • Coaching. Una sesión excelentemente moderada. Hubo un intercambio sano de ideas y experiencias, bastante refrescante. Allí me llamó la atención la intervención de Emma (¡dios, no se su twitter, que alguien me ayude, que quiero seguir a esta chica), según entendí es ScrumMaster quemada de muchas guerrillas en su empresa.
  • Software Craftsmanship. Propuesto por Xavi Gost y @ecomba. Bueno, nos contaron los principios del manifiesto y un poco como funcionaban, todo lo de los maestros y aprendices y demás. Me he leído el manifiesto y puedo decir que suscribo todo lo expuesto en él, ¿quién no podría estar de acuerdo? Es como los diez mandamiento, que aunque no seas judeocristiano, es probable que los pudieras firmar sin cargos de conciencia. Por ejemplo, el tema de educar mediante un sistema de maestro/aprendiz es obviamente la mejor manera de transmitir conocimientos. Por otro lado me sigue sin gustar el nombre o la analogía, ya que en el desarrollo de software no se construye nada. Los artesanos construyen pero en el software no se construye, se diseña. El que construye es el compilador. Por eso me sigue gustando más hablar de nosotros como ingenieros, y no como artesanos, porque nosotros diseñamos y el compilador construye. No veo por que el término ingeniero debe ser “evil”. En fin, es un tema de nomenclatura, porque a nivel de valores, que es lo que importa, me encuentro muy cercano a ésta forma de pensar. Un punto negativo en esta sesión fue que la moderación no fue muy efectiva, hablaron sobre todo Xavi y @ecomba, y la charla fue muy unidireccional (como me señaló alguien que no quiero nombrar).
  • La batalla de los frameworks. De nuevo con Xavi Gost, @ecomba y con la colaboración inestimable de @rcanalesmora en el papel del malo maloso de la película. Yo me lo pasé pipa y participé bastante, sobre todo para meterle caña a @rcanalesmora. Yo en esta estoy del lado del frente artesano y creo que los frameworks son armas de destrucción masiva a manejar con cuidado y mucha responsabilidad. El bando opuesto defendía que los frameworks te permitían contratar “personal poco cualificado” (a.k.a. “monos”) en masa para hacer los proyectos y ganar mucho dinerito calentito. El señor @rcanalesmora hizo una interpretación torticera de unas palabras que le dije la noche anterior, entre vinitos, y yo le respondí dándole cañita brava (pero con cariño).
  • Manipula. La señorita @rlaina nos dio una charla sobre el tema seguido de un debate. Tanto Emma como @jmbeas contaron sus problemas, que se parecen sospechosamente a los que tenemos todos. El problema es que cuando el debate se ponía interesante se nos acabó el tiempo y ¡ rompimos el timebox ! Cometimos el feo pecado de extender la charla a pesar de los heroicos esfuerzos de @amaliahern de salvar nuestras almas. La verdad es que el debate estuvo interesante, pero nos saltamos media comida.
  • Como tratar con locos paranoicos. Propuesta y dirigida por Xavi Albadalejo fue una de las charlas más interesantes. Usando la técnica de identificación de causas raíces analizamos porqué muchas personas recurren al Command & Control (a.k.a. “aquí se hace lo que yo digo”). Llegamos a la conclusión de que existían dos tipos de jefes autoritarios: los paranoicos y los desconfiados. Si te toca el primer tipo es mejor plantearte si dejar la empresa, ya que es una señal de que la cultura empresarial fomenta este tipo de personas como jefes. Si te toca el segundo tipo entonces hay esperanza. Lo curioso es que las técnicas que fuimos sacando para desactivar el ordeno y mando de un jefe desconfiado es simplemente… usar Scrum Efectivamente las técnicas propuestas por Scrum tienen como resultado aumentar la transparencia y esto acaba por aumentar la confianza. Muy enriquecedora esta sesión.
  • Diseño ágil. En esta sesión estuve bastante activo. Hablamos de como conseguir un buen diseño OO sin tener que hacer un diseño detallado al principio del desarrollo, práctica que va en contra de los principios ágiles. Yo conté mi experiencia, de que usando sólo TDD junto con un refactor continuo y agresivo, llegas a un diseño bueno. Sólo necesitas hacer al principio un pequeño diagrama de “cajitas” con la arquitectura de alto nivel. El detalle de cada “cajita” te la da el TDD+Refactor. También hice notar que esto por otro lado no te resolvía aspectos como el rendimiento y la seguridad, que tal vez sí hay que planearlos más de antemano. Algunos compañeros comentaron que ellos ponían pruebas de rendimiento en el build nocturno y que pedían a un experto en seguridad, que atacara el entorno de demo cuando el quisiera. Una aportación muy interesante. También se habló de las revisiones de código, y hubo unanimidad en que eran necesarias. Yo sostuve que la programación por parejas era equivalente a una “revisión de código continua”. Un asistente comentó que además ellos tenían un “moderador de proyecto”, que era externo al proyecto y neutral al equipo, que hacía revisiones de código. A mi me pareció una excelente idea. Ellos usaban un plugin de JIRA llamado Crucible, para hacer dichas revisiones. Lo tengo que probar. Todos estuvimos de acuerdo en que el UML era bueno para hacer bocetos de ideas y arquitectura de alto nivel. También consensuamos que había que hacer toda la documentación técnica necesaria para hacer el mantenimiento del sistema, pero por lo general, cuando el código está bien diseñado, éste es bastante legible, y necesitas menos documentación de la necesitarías normalmente.

Por supuesto hubo otras charlas, que supongo cada uno habrá comentado en su blog. Finalmente vino la retrospectiva, dinamizada, de forma genial, por el señor Ariel Ber (no se si tiene twitter). Se habló de las “AgileGirls”, de hacer un “Scrumbar” y de hacer el siguiente Open más grande. En concreto yo creo que una mejora para el AOS2011 debería ser que la gente participara más, me explico. En muchas sesiones la charla estuvo dominada por los “pesos pesados” y los “gurus” y vi a la gente un poco cohibida, lo que creo que impidió que se generara más debate. Yo mismo soy culpable de ese pecado y en muchas sesiones me hubiera gustado participar más, pero no me atreví. Tal vez en el siguiente open solucionemos eso.

Por supuesto hubo una segunda noche de fiesta, comimos en el “divinus”  y terminamos trasegando mucha cerveza y sangría en el “Ovella negra” (¿?) mientras @ydarias y compañía hacían lobby para que la siguiente AOS se celebre en Canarias (¡yo voto por vosotros!)

Quiero hacer mención especial de @amaliahern, que estuvo de fotógrafa e iba de sesión en sesión procurando que se cumpliera el horario. También creo que Ariel Ber estuvo excelente durante la retrospectiva, como comenté anteriormente.

Por último quiero poner mi opinión sobre un debate “silencioso” que se produjo de forma soterrada durante muchas sesiones: certificaciones sí, certificaciones no. De hecho hubo una sesión explícita sobre las certificaciones. Yo personalmente me he certificado CSM con Alan Cyment y Ariel Ber. Os recomiendo su curso, aprendí más sobre el espíritu de Scrum que en cualquier libro. Entiendo perfectamente a los que les da miedo que las certificaciones se perviertan y terminen siendo un modo fácil para que las grandes empresas se pongan el sellito de ágil y vendan por ahí lo que no son. También se corre el riesgo de que el agilismo quede preso de grandes consorcios certificadores. Sin embargo queda claro en todos sitios lo que significa ser CSM: que simplemente has dado un curso y que has comprendido lo que es Scrum, nada más. De comprender lo que es Scrum a practicarlo de verdad hay un mundo. De hecho la certificación que dice que usas Scrum con éxito no es la CSM, sino la CSP, en la que debes acreditar experiencia y pasar un “peer review”. De todas formas, sería de incautos juzgar la experiencia de alguien sólo por su certificación. Creo que los certificados son necesarios, pero no indispensables, y que tienen sus peligros, pero no por ello se debe generar un sentimiento de animosidad entre ambos bandos. Pensad, ¿se necesita ser universitario para ser un buen desarrollador de software? Por supuesto que no ¿Debemos abolir los títulos universitarios de ingeniería de informática por ello? Rotundamente no, aunque algunos políticos quieran. Pues lo mismo con las certificaciones.

En fin señor@s, yo vengo muy contento del AOS2010, y espero un AOS2011 aun mejor, tal vez en Canarias ;-)

P.S. Las fotos que tengo mejor no las cuelgo, curiosamente parecemos todos unos borrachos por la noche…

P.S.S. Señor @jmbeas péguele un tiro en la cabeza a su zombi, es para que no sufra el pobre :-D

Read Full Post »


Tras dos o tres semanas sin escribir, aquí vuelvo de nuevo continuando con la serie sobre como hacer consultas sin SQL. Resumiendo el anterior post, vimos que los sistemas clave/valor, aunque muy escalables, eran algo limitados desde el punto de vista de los lenguajes de query, ya que sólo permiten consultar por clave, y no ofrecen un lenguaje de query genérico. Para solucionar esto, propuse definir las consultas como objetos de primera clase dentro de nuestro modelo de negocio OO y persistirlos directamente. Sin embargo esto implicaba que cada vez que modificáramos la información de nuestro sistema habría que recalcular, de forma incremental, los cambios en el resultado de la consulta y volverlos almacenar. En concreto exploramos las distintas estrategias que se pueden seguir, y como afectan éstas tanto al rendimiento como a la posible falta de consistencia de las consultas con respecto a los datos actuales. Sin embargo, no conté nada sobre cómo almacenar las consultas en un soporte persistente siguiendo el paradigma clave valor, y ser capaz de recuperar el resultado de forma eficiente después. Nos ocuparemos de ésto en el presente post.

Empecemos por el caso más sencillo. Éste se produce cuando tenemos consultas que no dependen de parámetros externos. En estos casos podemos tener consultas con una función de selección arbitrariamente compleja, pero ésta está definida completamente y no admite parámetros. En SQL esto sería equivalente a una sentencia SELECT con una WHERE sin parámetros. Ejemplo, todos los varones mayores de edad y “ricos”:

SELECT *
FROM PERSONAS
WHERE EDAD > 18 AND SEXO = 'VARON' AND SUELDO > 25000 ORDER BY EDAD

En este caso sólo tenemos que darle un nombre único a esta consulta. Como estamos modelando la consultar como un objeto propiamente dicho, podemos usar el nombre de dicha clase como clave, y almacenar bajo esa clave todos los resultados. Usando notación JSON, el resultado de almacenar la query anterior podría ser por ejemplo:

{
  'query:varonesMayoresEdadRicos':{
    'totalCount':2344,
    '1':{
      'key':'k1',
      'nombre':'Juan',
      'edad':18,
      'empresa':'Acme S.A.',
      'sueldo':30000,
      'sexo':'VARON',
      'tfno':4234564
    },'2':{
      'key':'k76',
      'nombre':'Pedro',
      'edad':19,
      'empresa':'Acme S.A.',
      'sueldo':45000,
      'sexo':'VARON',
      'tfno':42321564
    },'3':{
      'key':'k34987',
      'nombre':'Eduardo',
      'edad':19,
      'empresa':'Acme S.A.',
      'sueldo':30000,
      'sexo':'VARON',
      'tfno':7664666
    }
// ....Restantes resultados...
  },
  'query:empleadosEnEdadDePrejubilacion': {
    // ....Restantes resultados...
  },
  'query:empleadosConPocoSueldo': {
    // ....Restantes resultados...
  }
// Otras consultas....
}

Nótese que hemos usado el nombre de la consulta como clave, y como valor hemos almacenado un objeto con varios campos. El primero, totalCount, es el número total de objetos que contiene la consulta. En este sentido tenemos libertad para almacenar otros campos con metainformación adicional, y con resultados estadísticos. Por ejemplo: clave del primer resultado, del último resultado, media de edad, sueldo máximo y mínimo, etc. Después tenemos los resultados propiamente dichos, usando un campo por cada objeto resultante de la consulta, que como nombre tiene el número de orden dentro de la consulta. El valor de estos campos es cada objeto, completo con todo sus campos.
El almacenar el objeto completo es desnormalizar nuestro almacenamiento de datos, con lo que vamos a duplicar información con el consiguiente gasto de espacio en disco. Este enfoque tiene la ventaja de mejorar el rendimiento a la hora de leer, debido a que el objeto completo se encuentra almacenado en la propia consulta. Podemos por otro lado ahorrar espacio sacrificando algo el rendimiento. Para ello no almacenamos los objetos completos, sino sólo sus claves. Esto está más en consonancia con un enfoque “normalizado” al almacenamiento de datos. La cosa quedaría por ejemplo:

{
  'query:varonesMayoresEdadRicos':{
    'totalCount':2344,
    '1':'k1',
    '2':'k76',
    '3':'k34987'
// ....Restantes resultados...
  }
}

En este caso, cuando recuperemos la consulta, tendremos que hacer un acceso extra al almacén donde están guardadas las entidades para traernos los datos de cada una de ellas. En el fondo esto no es más que un “join” hecha manualmente.
De nuevo hay que ver que nos interesa más, si desnormalizar y duplicar información, o ahorrar espacio de almacenamiento y hacer el “join”. Lo bueno es que podemos elegir que opción queremos.

En muchos otros casos nos encontraremos con consultas donde no queremos recuperar el objeto entero sino sólo algunos de sus campos.

SELECT NOMBRE, EMPRESA
FROM PERSONAS
WHERE EDAD > 18 AND SEXO = 'VARON' AND SUELDO > 25000

En estos caso el enfoque “desnormalizado” tiene más sentido:

{
  'query:varonesMayoresEdadRicos':{
    'totalCount':2344,
    '1':{
      'nombre':'Juan',
      'empresa':'Acme S.A.'
    },'2':{
      'nombre':'Pedro',
      'empresa':'Acme S.A.'
    },'3':{
      'nombre':'Eduardo',
      'empresa':'Acme S.A.'
    }
// ....Restantes resultados...
  }
}

Algunos os preguntaréis porque almaceno los resultados como “campos”. Un enfoque más intuitivo sería el de crear un único campo ‘resultados’ que fuera un array de objetos o de claves. El problema de este enfoque es que cuando fueras a recuperar la consulta tendrías que deserializar todo el array completo. Esto sólo es práctico con consultas con pocos resultados, pero no con las que tengan miles o millones de resultados. Algunos sistemas clave/valor, como BigTable o Cassandra, permiten recuperar los objetos parcialmente, es decir, traerte sólo unos campos del objeto y otros no. En estos sistemas el diseño que propongo nos permite traer los resultados de uno en uno o paginando. Esto no sería posible si almacenara los resultados en un único campo de tipo array. Sin embargo, ¿qué ocurre si mi sistema clave/valor me obliga a traerme el objeto entero? En este caso debemos cambiar de diseño. Veamos:

{
  'query:varonesMayoresEdadRicos:globalData':{
    'totalCount':2344,
    // Otros datos globales y metainformación de la consulta....
  },
  'query:varonesMayoresEdadRicos:1':{
    'key':'k1',
    'nombre':'Juan',
    'edad':18,
    'empresa':'Acme S.A.',
    'sueldo':30000,
    'sexo':'VARON',
    'tfno':4234564
  },
  'query:varonesMayoresEdadRicos:2':{
    'key':'k76',
    'nombre':'Pedro',
    'edad':19,
    'empresa':'Acme S.A.',
    'sueldo':45000,
    'sexo':'VARON',
    'tfno':42321564
  },
  'query:varonesMayoresEdadRicos:3':{
    'key':'k34987',
    'nombre':'Eduardo',
    'edad':19,
    'empresa':'Acme S.A.',
    'sueldo':30000,
    'sexo':'VARON',
    'tfno':7664666
  }
// ....Restantes resultados...
}

O si preferimos un enfoque normalizado:

{
  'query:varonesMayoresEdadRicos:globalData':{
    'totalCount':2344,
    // Otros datos globales y metainformación de la consulta....
  },
  'query:varonesMayoresEdadRicos:1':'k1',
  'query:varonesMayoresEdadRicos:2':'k76',
  'query:varonesMayoresEdadRicos:3':'k34987'
// ....Restantes resultados...
}

El truco en ambos casos es usar una clave y/o “fila” por cada resultado de la consulta, y una entrada adicional para la metainformación. De esta forma podemos paginar la consulta en cualquier sistema clave valor que soporte accesos por rango de claves. La última dificultad reside en si estamos usando sistemas clave/valor que no soportan accesos por rango de claves. En estos sistemas sólo podemos acceder a las claves de una en una. Para solucionar este problema podemos usar otro diseño en nuestra persistencia de las queries. Usando un enfoque “normalizado”:

{
  'query:varonesMayoresEdadRicos:globalData':{
    'totalCount':2344,
    // Otros datos globales y metainformación de la consulta....
  },
  'query:varonesMayoresEdadRicos:page1':{
    'pageSize':3,
    'nextPage':'query:varonesMayoresEdadRicos:page2',
    '1':'k1',
    '2':'k76',
    '3':'k34987'
  },
  'query:varonesMayoresEdadRicos:page2':{
    'pageSize':3,
    'prevPage':'query:varonesMayoresEdadRicos:page1',
    'nextPage':'query:varonesMayoresEdadRicos:page3',
    '1':'k256g',
    '2':'k365',
    '3':'k487'
 }
// ....Restantes páginas de resultados...
}

En este caso tenemos el trabajo adicional de preparar las páginas al persistir la consulta. Cada página es una fila o clave dentro del almacén de persistencia. Además de los resultados tenemos campos indicando el tamaño de cada página, y las páginas anteriores y siguiente.

Hasta ahora nos hemos preocupado por las consultas sin parámetros. Sin embargo normalmente tenemos muchas consultas que son parametrizables. Como ejemplo, varonesMayoresEdadConSueldo(sueldo: float). En SQL:

SELECT *
FROM PERSONAS
WHERE EDAD > 18 AND SEXO = 'VARON' AND SUELDO = #sueldo:FLOAT# ORDER BY EDAD

En este caso la solución es tratar esta consulta como si fueran N consultas sin parámetros, cada una representando cada uno de los posibles valores del parámetro. Obviamente hay infinitos sueldos posibles, pero sólo nos interesan los posibles valores de éste que contengan datos en nuestro sistema. Usando un enfoque normalizado, y usando una fila o par clave/valor por resultado, el ejemplo quedaría como sigue:

{
  'query:varonesMayoresEdadConSueldo:00002345600:globalData':{
    'totalCount':344 // Resultados con sueldo igual a 23.456 euros
    // Otros datos globales y metainformación de la consulta....
  },
  'query:varonesMayoresEdadRicos:00002345600:000000001':'kX3456GF',
  'query:varonesMayoresEdadRicos:00002345600:000000002':'k76dfw',
  'query:varonesMayoresEdadRicos:00002345600:000000003':'k349sd8s7',
// ....Restantes resultados para sueldo igual a 23.456...
  'query:varonesMayoresEdadConSueldo:00012000075:globalData':{
    'totalCount':2 // Resultados con sueldo igual a 120.000,75 euros
    // Otros datos globales y metainformación de la consulta....
  },
  'query:varonesMayoresEdadRicos:00012000075:000000001':'kZZZZ',
  'query:varonesMayoresEdadRicos:00012000075:000000002':'k666f3'
// ....Restantes resultados con otros sueldos...
}

La forma en que construyo las claves es curiosa. Cada clave es la concatenación, separado con ‘:’ de ‘query’, el nombre de la consulta, el sueldo y el orden en que deben aparecer (por edad). Como en realidad estoy usando claves que son strings, el orden de almacenamiento es lexicográfico. Para que el orden coincida con el sueldo he tenido que normalizar el sueldo en formato string, rellenando con 0s y reservando las dos últimas posiciones para los decimales. También he formateado el orden por edad. De esta forma las filas se almacenan por orden, principalmente por sueldo y secundariamente por edad. ¿Por qué molestarme en todo esto? Al fin y al cabo sólo quiero buscar las personas con un sueldo concreto. Desde este punto de vista tiene sentido formatear el orden por edad, pero no el sueldo, para conseguir paginación. La respuesta es sencilla, si formateamos el sueldo, la siguiente query sale gratis:

-- Todos los varones mayores de edad que ganan menos de un sueldoMáximo
SELECT *
FROM PERSONAS
WHERE EDAD > 18 AND SEXO = 'VARON' AND SUELDO < #sueldoMaximo:FLOAT# ORDER BY EDAD

Y también:

-- Todos los varones mayores de edad que ganan más que un sueldoMinimo
SELECT * FROM PERSONAS WHERE EDAD > 18 AND SEXO = 'VARON' AND SUELDO > #sueldoMinimo:FLOAT# ORDER BY EDAD

Nos basta con hacer una consulta por rango de claves para implementar ambas queries. Con lo que simplemente una vez que tenemos una query paramétrica con el operador igual obtenemos gratis las consultas con el operador mayor y menor, sólo necesitamos actualizar la primera de las tres. Por supuesto esto sólo va bien si nuestro sistema clave/valor admite consultas por rangos de claves, como es el caso de REDIS, Toky Cabinet’s B+Tree, BigTable y Cassandra. Si no tenemos esta capacidad entonces tendremos que tratar estas queries con mayor y menor como independientes de la primera, aunque otras soluciones más sofisticadas son posibles.

Algunos sistemas clave/valor te permiten definir como quieres interpretar las claves, si como números, fechas, strings, etc. De esta forma podríamos simplificar la forma de montar las claves, y tratarlas como numéricos. Hay que tener claro como va a tratar nuestro sistema de almacenamiento las claves y que capacidades de query tiene, para diseñar de la mejor manera nuestro esquema de almacenamiento de consultas. Por ejemplo, Cassandra nos permite:

  • Definir filas con claves ordenadas dentro de una “column family”. Las filas se almacenan por orden.
  • Queries por rango de claves. Como las filas se almacenan por orden las consultas por rango son eficientes.
  • Recuperar parcialmente una fila (sólo algunas columnas o campos).
  • Ordenar las columnas o campos de una misma fila, y traerse rangos de columnas. La query de rangos de claves se puede combinar con la de rangos de columnas.
  • Definir el orden de las filas y las columnas usando distintas funciones de ordenación: numérico, lexicográfico, fechas, etc.

Como vemos Cassandra es bastante potente y flexible. Usándolo podemos crear un esquema de almacenamiento flexible y eficiente como el que sigue:

{
// KeySpace: representa todo el esquema global de almacenamiento
  'query:varonesMayoresEdadConSueldo': {
  // ColumnFamily, cada fila tiene como clave el sueldo, ordenado numéricamente (float)
  // Las filas se almacenan por orden y lo más próximas posibles en disco
    '23456': {
    // Fila, tiene N columnas, ordenadas numericamente. Cada nombre de columna es el orden en la consulta (por edad)
    // El valor da cada columna es la clave de la persona
      '1':'kX3456GF',
      '2':'k76dfw',
      '3':'k349sd8s7'
    // Otros resultados para este sueldo
    },
    '120000.75': {
      '1':'kZZZZ',
      '2':'k666f3'
    },
// ... otros sueldos ...
  }
// Otras column families: otras queries, datos de persona etc.
}

Mucha gente se enfrenta a Cassandra por primera vez sin haber reflexionado sobre como hacer queries complejas con él. Por esto suelen exclamar “What the fuck !” cuando ven el modelo de KeySpaces, ColumnFamilies, SuperColumnFamilies y demás. Simplemente intentan hacer analogías con las “tablas de toda la vida”, y se pierden que una motivación importante es realizar esquemas de almacenamiento de consultas flexibles y eficientes. Como podéis apreciar si lo pensamos desde el punto de vista de implementar consultas, toda esta historia de ColumnFamilies tiene perfecto sentido:

  • Una ColumnFamily para almacenar entidades.
  • La ColumnFamily nos sirve para almacenar queries paramétricas, usando el enfoque normalizado, como hemos visto antes.
  • Si queremos desnormalizar la consulta, podemos usar SuperColumnFamilies

Si añadimos sus capacidades de escalabilidad, tolerancia a fallos y CAP ajustable, yo veo a Cassandra y BigTable una opción muy potente a las bases de datos tradicionales.

Como hemos visto somos capaces de reflejar de forma persistente, y eficiente, el resultado de un consulta cualquiera. Sin embargo todo esto está muy bien cuando tenemos una aplicación concreta, en la que sabemos que se van a realizar una serie concreta de consultas. Es decir, que el conjunto de consultas a realizar está prefijado. Este es el caso de la mayoría de las aplicaciones de hoy en día, dado un funcional, o un conjunto de historias de usuario, podemos definir y programar todas las queries necesarias para nuestro sistema.

Existe otro tipo de aplicaciones donde este conjunto de consultas no se sabe de antemano. En esta clase de aplicaciones el usuario final puede definir mediante algún lenguaje de query (textual o visual) cualquier consulta que se le ocurra. Es el caso del data warehousing y el business intelligence o el data mining. ¿Cómo podemos implementar este tipo de sistemas si no tenemos SQL? Al fin y al cabo no podemos pedirle al usuario que programe las consultas él con nuestro lenguaje de programación favorito ¿Debemos usar en este caso un sistema especializado? Bien, esto último es una buena opción, pero no es la única… cómo veremos en mi siguiente post.

Read Full Post »

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 41 seguidores