Showing posts with label diseño. Show all posts
Showing posts with label diseño. Show all posts

Thursday, September 18, 2008

Engañemos a la realidad

Hace un tiempo, hablábamos de Richard Feynman. Feynman fue una persona notable, pero no haremos otra biografía suya aquí, la red está llena de ellas. Un comentario que dejé en un post de Dirección de Proyectos hablando de él fue seguido por una magnífica entrada en ese mismo blog sobre lo que un físico puede aportar a un gerente de proyectos, y eso me motivó a seguir hablando del tema.

Feynman participó en la comisión que investigó el desastre del Challenger, y luego de un informe impecable, concluyó con una frase que debería ser leída y releida por cualquier persona vinculada la gestión de proyectos: "para crear tecnología exitosa, la realidad debe prevalecer sobre las relaciones públicas, porque la naturaleza no puede ser engañada"


Esa frase, que deberíamos tener tatuada en el cerebro, es el corolario del informe que decíamos, donde profundiza sobre las causas últimas del desastre del Challenger. La historia se cuenta en una excelente entrada del blog Historias de la Ciencia. Para los que no tengan ganas de leer el post de Historias de la Ciencia, se puede ensayar una explicación rápida: el Challenger explotó porque las juntas toroidales (unos anillos que aislaban los segmentos de los cohetes aceleradores de combustible sólido) perdían su resistencia cuando antes del lanzamiento eran expuestas al frío (por ejemplo, al frío de una mañana de invierno), cosa que la NASA sabía (o al menos, tenía buenos motivos para intuir).

Para los que no tengan ganan de leer el post de Historias de la ciencia y piensen que con los dos renglones anteriores alcanza, vayan y lean el post de Historias de la ciencia: es uno de los mejores post de uno de los mejores blogs


Ahora bien, como bien nota Feynman, las juntas toroidales no aparecieron ahí por arte de magia. Tampoco es que hayan estado funcionando como violines bien afinados antes del desastre. Feynman dice que bajo este hecho (esto es, la erosión de las juntas), se escondían unas cuantas debilidades organizacionales (desidia, presión por mostrar resultados), que, en definitiva, crearon el medio ambiente donde el problema de las juntas toroidales pudo aparecer y desarrollarse hasta ser un gran desastre.


El informe en minoría de Feynman en la comisión Rogers me genera cierta sensación de familiaridad, no porque haya trabajado en la NASA o porque me haya explotado algún que otro transbordador espacial. Por supuesto que el recorrido que haremos aquí del informe es liviano y no es excusa para dejar de leerlo entero.


A continuación, las partes del informe que más me han chocado.


El informe cita a los oficiales de la NASA diciendo que, dado que el transbordador es un vehículo tripulado "la probabilidad del éxito de la misión está necesariamente cercana a 1". Esto se llama wishful thinking y nos genera cierta perplejidad que la NASA diga que algo es seguro porque dado que lleva humanos dentro, debería ser seguro. Luego de la sensación de repulsión que nos produce tal muestra de pensamiento irracional de nada menos que los oficiales de la NASA, pensemos las veces que hemos esperado que un software funcione solo porque si no funciona nos echan a todos.


Sigue mencionando Feynman que las juntas toroidales ya habían mostrado erosión en vuelos anteriores. Dado que la erosión solo había llegado a un tercio del radio de las juntas, se concluyó que se estaba operando con un coeficiente de seguridad de tres (la erosión tenía que triplicarse para que la junta cediera). Feynman dice que "el equipo no estaba funcionando como se esperaba, y existe por lo tanto el riesgo de que pueda operar con desviaciones aún más grandes y no completamente comprendidas. El hecho de que un peligro no haya llevado a una catástrofe anteriormente, no garantiza que no lo hará la próxima vez, salvo que éste sea completamente comprendido". Otra vez la NASA dando muestras de wishful thinking y desidia. De nuevo, al asombrarnos de la falta de rigor ajeno, no perdamos de vista que, al mirar comportamientos anómalos del software que desarrollamos, solemos tranquilizarnos pensando que igual son casos esporádicos o poco comunes, o que hay mucho margen para ese aumento de memoria tan raro.


Más allá de las juntas toroidales, el informe en minoría (el informe de Feynman fue en minoría: solo lo firmó él) se ocupa del diseño del motor principal del Challenger, aparentemente no involucrado en el desastre. La forma de diseñar motores de este tipo consiste en ir ensamblando los componentes en una estrategia bottom-up, de forma que cuando se detecta un problema en un componente a un determinado nivel de integración, se puede corregir antes de pasar al próximo nivel de integración. Si algún problema surge en algún momento, se puede aislar y corregir, porque el problema está en aquello que se ha incorporado en el presente paso. El diseño y desarrollo del motor principal del Challenger siguió un procedimiento top-down: el motor se diseñó y se lo ensambló todo junto con pocas pruebas unitarias, lo que dificultó el proceso de encontrar y corregir errores.

Adicionalmente, los procedimientos de certificación se volvieron dudosos: una turbina con una fisura luego de un determinado tiempo de prueba en el banco de pruebas representa una prueba no superada para la FAA. Bueno, para la NASA si las fisuras no habían llevado a una fractura, la prueba había sido exitosa. El que nunca haya participado en un proyecto donde no existían pruebas unitarias, donde los componentes no se podían probar en forma aislada y donde se había redefinido el concepto de 'prueba superada' que tire la primera piedra.


Y por último, el hardware y software de aviónica. El proceso de prueba y verificación del software era estricto y seguro (más allá de que algún iluminado quería recortarlo porque tanta seguridad no era necesaria: la prueba de que tanta seguridad puesta en el desarrollo del software no era necesaria era que el software nunca había generado ningún problema), pero había un problema: el proceso de cambio en el software era tan complicado, que nadie se animaba a reemplazar el hardware y éste, para 1986, ya era obsoleto. No se si alguien le suena esta situación. A mí sí.


No pretendo yo agotar las lecturas de un informe producido por muchos años de experiencia de alguien de la talla intelectual de Feynman. Sin embargo, su lectura nos ha servido para tratar de extender por la organización los siguientes conceptos:


  • No pienses que las cosas van a salir bien porque no te merecés las consecuencias de que salgan mal.
  • No decidas que cierta anomalía es perfectamente aceptable y que no vas a invertir esfuerzo en corregirla sin antes explicar y explicarte el mecanismo que lleva a la anomalía observable. Puede haber una bomba atómica bajo la alfombra.
  • Diseñá de abajo hacia arriba, probando cada componente independientemente y preparate para el proceso de debug, que va a hacer inevitable.
  • Diseñá para el cambio: la plataforma de hoy tendrá que ser reemplazada mañana.
Y se honesto, porque, al fin y al cabo, la naturaleza no puede ser engañada.

Friday, September 12, 2008

Diseñando experimentos o experimentando diseños

Hubo un tiempo que fue hermoso, y fui libre de verdad. En ese tiempo era un técnico, me perdía entre arquitecturas y optimizaciones, y las fechas, rentabilidades, calendarios, ofertas no eran mi problema. Mi responsabilidad se limitaba a aquellas cosas que hacía yo directamente. Estaba en la gloria. Además, como solo mi trabajo era mi responsabilidad, podía hacerme el vivo.

Magnificadas y embellecidas por la memoria, de esa época tengo recuerdos como el que sigue.

Una programadora del equipo se acercó a D., intuyendo lo peor. No se por qué se acercó a D. , siendo que yo era la interfaz más amable de ambos y nos sentábamos uno al lado del otro (yo era algo así como el arquitecto de aplicaciones y D. el de base de datos). Ella, que sabía lo que seguía, le dijo a D.: "esta query anda lenta" y se dispuso a aceptar con resignación su destino. D. ni siquiera levantó la cabeza del monitor y dijo "ahá. y que querés hacer..." (D. siempre citaba a Tom Kyte diciendo "no optimices queries, comprendé la pregunta").


Hasta aquí, la morocha (dije que la programadora era morocha?. Bueno, era morocha y de ojos verdes) tenía una oportunidad. No es que D. fuera muy amable, tampoco es que la curvilinea figura de esta chica lo conmoviera, sino que él siempre intentó ser justo. Ella no se dio cuenta que tuvo una oportunidad de ser rediminda y dijo "no, es que quería ver si desde la base podíamos hacer algo para que ande más rápido". Era exactamenet lo que D. estaba esperando para reconfirmar que toda oportunidad otorgada es una pérdida de tiempo: "algo así como poner el parámetro turbo=true? no, todavía no existe. Te aviso cualquier cosa".


Se puede decir que D. era (es) un tipo áspero. Que no hay necesidad de andar presumiendo de lo que uno sabe, porque uno en algún momento uno tampoco supo. Pero creo que en esta oportunidad el problema no era con la ignorancia, sino con un concepto problemático: la performance no importa, la performance se agrega después, es como la cobertura de un helado, y además es algo que depende del DBA.


A D. lo irritaba esa postura. A mí también. El origen de mi molestia tiene que ver con que considero que no se puede evitar el pensar la arquitectura al principio del proceso de desarrollo del software (lo que no implica, claro, que uno no tenga que pensarla durante todo el proceso), no importa cuan ágil sea uno (de todas formas, el paso de planear la arquitectura se evita más por desordenado que por ágil, se reconoce desde la comunidad ágil que la fase de architecture envisioning es necesaria), particularmente si uno no quiere encontrarse al final del proyecto con que algo anda más lento de lo esperado, o algo peor.


Se podría argumentar que ese no es un problema hoy en día: hay arquitectos, fases para definir la arquitectura, sectores de arquitectura, libros sobre arquitectura y toda una parafernalia sobre el asunto. Me permito pensar que todo eso, o bien es insuficiente, o bien está mal dirigido.


Volvamos al principio, que es la arquitectura?. Yo tengo una definición de arquitectura: “la arquitectura de un sistema es el conjunto de decisiones de diseño que tienen un impacto profundo en la calidad del mismo” (en realidad, como todos las cosas que valen la pena, ya alguien la pensó antes).


Por eso, creo que la fase de pensar la arquitectura, de decidir las características más importantes del sistema antes de comenzar la construcción y no esperar que las decisiones tomadas por un grupo de desarrolladores sobre la marcha sean acertadas, es ineludible. De todas maneras, creo que una vez más conviene bajar a detalle y preguntarse que significa obtener una visión de la arquitectura. No creo que unos modelos en papel alcancen para tomar decisiones importantes, particularmente cuando uno incorpora componentes nuevos (no recuerdo ningún proyecto en los últimos años en los que no haya tenido por lo menos un componente nuevo). La solución viene dada por construir diseños experimentales que sometan a la arquitectura propuesta a condiciones extrapolables a las que soportará una vez implementada.


Las características que normalmente deseo observar al someter a una arquitectura a un experimento son:

  • El comportamiento bajo alta carga.
  • La posibilidad hacer andar en forma aislada los componentes, como forma de poder detectar y corregir problemas una vez que esté el sistema implementado.
  • El comportamiento ante situaciones límite, como caídas de equipos, operaciones maliciosas, errores en otros componentes.
  • La facilidad de implementar cambios en la arquitectura (reemplazar un componente por otro).
  • Cierta seguridad sobre la estabilidad de los componentes. Si bien no tengo demasiado cariño por la costumbre de gritar ‘hay un bug en Oracle’ ante el primer error que uno cometió en una query, me he encontrado con algunos productos open source con algún que otro error (un memory leak, por ejemplo). Y me he encontrado con que MS se pasa algunos estándares por el forro, y agrega algunas condiciones a las del estándar....
  • Y el más importante, nuestras suposiciones sobre el funcionamiento de lo que vamos a usar son razonables? O sea, podemos estar seguros que, al menos en lo importante, nuestro diseño no va en contra de algunas limitaciones de la tecnología?

Bien, el problema es que esto es mucho más fácil de enunciar que de hacer. Diseñar un experimento significativo y a la vez sencillo es una habilidad que requiere conocimiento, práctica, y hasta diría que talento. Si es difícil hacerlo, escribir unas guías sobre como hacerlo es aún más difícil, pero concluímos que hacer el esfuerzo (más adelante explico el por qué, además de porque consideramos que aporta valor) de explicitar algunas guías para definir experimentos valía la pena. Acá va un resumen de los pasos de la guía:

  1. Identificar las interfaces relevantes: pueden ser algunas pantallas, mecanismos de IPCs e integración (colas de mensajes, web services)
  2. De las pantallas identificadas, abstraer la presentación y pensar unicamente la interfaz de la operación
  3. Desplegar el software de base. Acá me refiero a desplegar todo el software de base, no un poco, particularmente si uno no lo conoce. Me refiero a base de datos, appl. server, broker de mensajes, todo.
  4. Implementar mocks de cada interfaz relevada. El mock, de acuerdo a cada caso, puede ser una clase que simplemente no haga nada y tenga un sleep o similar para simular el delay de procesamiento o una clase que haga algunas operaciones contra el soft de base, sin mayor lógica, simplemente para obtener tiempos reales de operaciones en el soft de base
  5. Conseguir un simulador de carga (o hacer al menos un par de scripts en ant, nant o shell) que tire transacciones.
A partir de ahí, ir variando las condiciones y ver que sucede (algunas ideas):
  1. Ejecutar lo construido en (5) y verificar límite de capacidad de procesamiento (ese momento donde las colas no entran en régimen: al margen, la utilidad de los modelos de colas para el análisis de performance, tema para otro post)
  2. Bajar intempestivamente un componente y ver cuan fácil es determinar el problema
  3. Determinar que posibilidad hay de ejecutar aisladamente un componente construido sin el software de base (obviamente, con máquina virtual y sistema operativo, unicamente)
  4. Medir el consumo de memoria de los componentes, particularmente de los nuevos (si pusimos un nuevo framework, medir el consumo de memoria en la máquina virtual, por ejemplo)

Un experimento (o prueba de concepto, o como se desee llamarlo) es un entorno controlado donde podemos generar variaciones planificadas y observar los resultados. Es sencillo de enunciar, pero diseñar un experimento razonablemente significativo es algo que requiere práctica, conocimiento del dominio y no se si talento natural ( sí, se que todo parece indicar que no somos una tabla rasa, pero tampoco es que don Pinker se haya impuesto por tanto).

Aún así, y dado que teníamos que cumplir, pensamos que sería útil hacer el esfuerzo de escribir un manual, o al menos unas guías, que ayuden a quien tenga que hacerlo a diseñar un experimento. Al fin y al cabo, que sea difícil de aprender, no significa que no valga la pena de ser enseñado, no?

Por qué todo esto?. Basicamente porque existe en CMMI L3 un área de proceso llamada ‘Decision Analysis and Resolution’. Como además de la certificación siempre tuvimos como norte que el proceso sirva, además, para que mejoremos la calidad de nuestros productos, nos atrevimos a perder tiempo pensando en el proceso de desarrollo además de la estrategia para el SCAMPI.

En la próxima parte de este post veremos como encaja esto en la process area de 'Decision Analysis and Resolution'.

Tuesday, August 26, 2008

Escolástica y Positivismo

A veces me sorprendo a mí mismo pensando que la evolución, tal como ha sido descripta por el genial Charles Darwin (y ampliada o explicada a la luz de la genética por el neodarwinismo), explica todo. Es decir, explica el funcionamiento de nuestro cerebro, explica la tendencia a la xenofobia, nuestra irracionalidad, y además nuestras cosas buenas, como el amor a nuestras crías.

Incluso más, como propone Stanislav Lem en 'Invencible', creo posible ámbitos de evolución no biológica: el capitalismo de mercado sobrevivió porque es el sistema mejor adaptado a la forma de ser de nosotros los humanos ( lo que no quiere decir que nos tengamos que quedar con su forma actual ), las ideas sobreviven por su adecuación a nuestra idiosincracia, y así unas cuantas cosas. Bueno, también lo propuso, creo que después, Richard Dawkins con sus memes

El mismo fenómeno de evolución se da con el software. El software que detestamos, ese que legamos y que queremos tirar, ha estado sobreviviendo al medio durante mucho tiempo. Eso no quiere decir que sea perfecto, la evolución no encuentra la mejor solución: encuentra una solución que permita que el organismo viva. Y eso ha hecho, ha sobrevivido. Con sus problemas, claro: no sabemos si un determinado código anda de casualidad o no, no nos animamos a tocarlo, es poco claro, si se hubiese pensado de una para el uso actual sería más claro, la tecnología es vieja, y unas cuantas cosas más. Pero aquí esta, y eso es una señal de que tiene algo de conocimiento valioso.

Por eso me irrito cuando se empieza un proyecto de reingeniería y todo el mundo está convencido que el nuevo software será mejor que el anterior, simplemente porque el anterior era una bosta. El anterior vivió muchos años, sobrevivió y se adaptó: el que no hicimos, todavía no demostró tanto mérito y no va ser fácil prepararlo para que lo haga, o, al menos, no lo haremos sin esfuerzo simplemente porque somos más vivos que los anteriores.

Por supuesto que parte de nuestros problemas para hacer algo bueno tiene que ver con las imperfecciones adaptativas del sistema anterior (que si no las tuviera, no lo estaríamos cambiando): no hay documentación, la tecnología es vieja y el código es poco claro, incluso subóptimo (a veces no es subóptimo, nos parece subóptimo porque lo evaluamos con poca información)

Y acá llegamos al punto donde quería llegar: la documentación. Estos sistemas legados no tienen documentación, o si la tienen, no podemos confiar en ella porque está desactualizada. Este es el estado general de las cosas, y creo que vale la pena preguntarse por qué con inexorable fatalidad la documentación falta o está desactualizada. Voy a hipotetizar algunos puntos:

  • La documentación se desactualiza porque no se percibe como útil. Y no se percibe como útil porque muchas veces no lo es: antes de documentar algo deberíamos preguntarnos si es más fácil leer la documentación que el código (he visto 'documentación' que ha sido el mismo programa hecho en pseudocódigo).
  • Aún cuando la documentación no esté desactualizada, siempre nos inunda la sospecha sobre su pertinencia al estado actual del sistema. Esto es, en parte, porque sabemos que la documentación tiende a desactualizarse, pero hay una causa subyacente ahí: cuando leemos documentación no tenemos más que el auxilio de la fe. Es decir, creemos que la documentación está actualizada porque alguien nos los aseguró, no porque podamos comprobarlo.

Diría que la misma presión del entorno que ha moldeado el sistema a su forma actual ha presionado para que la documentación se abandone. Bueno, y entonces qué? dejamos que el código se comente solo?. Yo creo que tenemos que adoptar una solución basada en criterios robados: es mejor almacenar experimentos que descripción de los resultados del experimento. Los físicos no lo pueden hacer: por ejemplo, el CERN no puede dejarnos jugar con su LHC un rato para ver lo que finalmente salga, pero nosotros tenemos una ventaja: podemos empaquetar el software junto con los experimentos que permitan ver si el mismo anda: test unitarios (como junit, nunit) y de regresión (como jmeter, selenium).

En mi opinión, los scripts de test son la única documentación que podremos utilizar sin temor a que esté desactualizada: el test se puede someter a prueba, se puede ejecutar, sirve para experimentar, mientras que la documentación se queda horriblemente corta al respecto. Incluso, me siento cómodo aplicando esta idea al desarrollo de requerimientos cuando lo que tenemos es un software que reconstruir: más que producir modelos en papel, preferiría producir scripts de test.

Lo anterior no es ni más ni menos que otro triunfo de la experimentación y el positivismo sobre la visión escolástica.

Friday, June 13, 2008

Que maestro!

Me gusta encarar proyectos de reingeniería de aplicaciones: tienen algo de candor iluminista (*) por eso de la confianza en el futuro, no se escucha el solapado o no tan solapado pedido del cliente de arreglá tres horas la mierda que nosotros hicimos en seis meses , y por supuesto, uno supone que se le ocurrirá una idea genial en diez minutos que será mejor que el diseño evolutivo que tiene encima una aplicación que hace quince años corre sin problemas ( tendencia innata al creacionismo, o a la irracionalidad, que es lo mismo, que tiene uno).

La idea de diseño evolutivo está tomada de la idea de la evolución de las especies: una aplicación que está andando, sufriendo cambios desde hace diez o quince años y que pasó por ocho presidentes, dos devaluaciones, cuatro incautaciones de depósitos, siete burbujas con sus correspondientes explosiones, doce cambios importantes en la legislación impositiva y miles de cambios menores ha hecho un fantástico trabajo de adaptación al medio. Claro que, en definitiva, el relojero es ciego, por lo que en el proceso adaptativo se generan características que hoy se revelan imperfectas, o directamente como boludeces. Algunas de estas imperfecciones evolutivas son imperfecciones porque cambió el entorno y lo que antes era una ventaja hoy no lo es. Otras siempre han sido malas pero la desventaja que suponen no ha sido incompatible con la supervivencia, todavía.
Así, haciendo arqueología en una aplicación que estábamos reingenierizando, dimos con una obra, no del relojero ciego, sino del diseñador corto. Llegamos a una tabla que se llamaba algo así como xtmaetr, y sus campos eran algo así como:
  • ID (bien, lógico, normal)
  • XTMAEDECR (puedo deducir que la descripción)
  • XTMAE_1
  • ...
  • XTMAE_9
Los ID eran correlativos, y el resto de los campos no tenían absolutamente ningún patrón: un mismo campo tenía una fecha en un registro, para pasar a tener una sucesión ininteligible de caracteres en el siguiente. Le preguntamos al padre de la criatura, quien orgulloso nos repondió: "esta tabla es el maestro". El maestro de qué? El maestro de todo, el master of the universe, alfa y omega, la real y lo posible, el sonido y la furia. A ver si me explico: esa tabla tenía todos los artículos, todos los clientes, todas las sucursales, todas las provincias, y así siguiendo, con un campo "tipo" que decía si estábamos en presencia de un cliente o de una pieza de repuesto de un motor diesel. Definitivamente, un maestro.

Una pena que no llevaran la idea un poco más allá y se limitaran a tener una tabla, como lo explica Tom Kyte: uno no tiene que diseñar nada, ni saber de diseño, ni conocer las características del software de base que está usando, simplemente tiene que hacer algo genérico.


(*) la entrada de la wikipedia de iluminismo no es correcta. Uso en este post a iluminismo como sinónimo de ilustración.

Tuesday, June 10, 2008

Lóbulo temporal

Quien convenció al mundo, o al menos a algunos IT Architects, que el uso de tablas temporales era una buena idea??!?.

No creo que sea una práctica extremadamente común (el mundo no funcionaría) pero me he encontrado con algunos casos: la última vez, el otro día.

El asunto era más o menos así: una típica aplicación de guarda-recupera-datos, que es lo que hacen, pese a la pretención de los amantes de las multicapas, la mayoría de las aplicaciones comerciales (la existencia de Business Objects que son solo un pasamanos, o peor aún, que implementan cosas que la base de datos hace mucho mejor, será tema para otro post).

La maravilla de la informática con la que mi triste figura dio el otro día tenía un bloque con la siguiente lógica (bueno, sabrán disculpar el exceso del lenguaje que significa llamar 'lógica' a eso):

  • El usuario identificado en la sesión podía ser de tipo A o B
  • Se generaba un número único de operación (con una secuencia)
  • Si el usuario era de tipo A, se obtenian datos de la Tabla A con una condición y se insertaban en la Tabla TMP agregándole a cada registro de la Tabla TMP el identificador generado anteriormente (así no se mezclaban los datos entre sesiones, no vayan a creer que era una chapuza)
  • Si el usuario era de Tipo B, insertaba igualmente en la Tabla TMP pero pero obteniendo los datos de la Tabla B
  • Para finalizar, hacía un join entre la Tabla TMP, filtrando por el ID de operación, y otra tabla, agregando proyecciones y otras cosas para finalizar mostrando unos pocos resultados.
  • Al finalizar, borraba, vía delete, los registros de la Tabla TMP con el ID de operación (obviamente, no podía hacer un truncate porque la tabla la usaban unas cuantas sesiones)
Este portento del ingenio humano en forma de aplicación informática transformaba así una consulta en dos consultas, más unos cuantos inserts, más idéntica cantidad de deletes. Eso sí, la muestra de destreza tecnológica tenía un motivo: la reutilización de código ( no es claro como se reutiliza el último query, más todos los deletes? Que no debieran existir es apenas un detalle que no debe opacarnos la alegría que nos causa reutilizar código), y la lógica de la aplicación, que haría que si esto fuera una única query sería demasiado complicada ( es claro que es mucho más sencillo seguir un código que hace en muchas líneas lo que se podría hacer en una ).

En la contemplación y admiración de tal muestra de capacidad e inventiva pasé unos días, la semana pasada: los gustos hay que dárselos en vida.