lunes, 26 de mayo de 2008

Una clase sobre buenos modales.

De vez en cuando una clase llega exactamente a donde tenía que llegar en el momento preciso. La mayoría de las veces si uno quería dar clase sobre "A" termina dando sobre "a" que se le acerca bastante, pero no era lo que uno quería enfatizar.

Hoy llegué justo al centro de una de las cuestiones más importantes del desarrollo de software, y quiero aprovechar que tengo fresco el camino. Antes es obligado agradecer a los alumnos por las preguntas y comentarios precisos en el momento justo. Gracias.

Comenzamos desarrollando un ejercicio enunciado - diseño - codificación que venía de la semana pasada. Es un pequeño sistema de liquidación de sueldos. Diseñamos sistema con un gráfico (DFD nivel 1). Voy rumbeando hacia el diseño final, que era bastante elaborado, no por lo complejo sino justamente por su simpleza.

Así que luego de un largo rato tenemos el diseño completo y pasamos a la codificación.

Codificamos una parte de un procedimiento del sistema, casi un boceto. Cuando está más o menos terminado digo la típica y odiada frase "termínenlo para la semana que viene y vengan con las dudas y problemas que se les hayan presentado. Por ejemplo acá faltan datos, tienen que agregarlos de alguna manera. ¿Cómo harían para obtener los datos del empleado y mostrarlos en el recibo de sueldo?".

Momento de tensión. Surgieron dos posibilidades, creo que yo sugerí una (seguramente la que no me gustaba, viejo truco) y alguien tiró la otra. Lo importante es que ambas funcionaban y parecían simples. Impulso una votación.

Esto es lo importante: una de ellas implicaba un cambio en el diseño, mientras la otra lo seguía. Recordemos el momento: ya habíamos cerrado el diseño hacía bastante tiempo. Eligieron la que cambiaba el diseño. Suerte para mí, porque me dio pie para el sermón.

"¡¿Por qué eligieron esa?! Me rompieron el diseño, estaba tan bonito, tan cerrado, ¿no estábamos todos de acuerdo?".

Y ahí empezó el cierre final, que fue muy bueno. Voy a trasladar los conceptos centrales:

Obviamente en un ámbito académico todos estamos aprendiendo, así que en ese contexto es un error. En la vida profesional elegir esa opción no es un error, sino una falta grave que merece castigarse físicamente. ¿Por qué tanto?

Durante el desarrollo del software, en algún momento se establece un diseño. Su elaboración será más o menos participativa, más o menos consensuada entre los miembros del equipo, o será impuesto autoritariamente. Pero se establece. Y punto. Si no nos gusta, ése era el momento de dar pelea, y si la dimos y perdimos, perdimos. Luego taza taza cada uno a su casa y a codificar.

Cuando codificamos debemos seguir el diseño a rajatabla. En la práctica, eso indica básicamente que si el módulo que me ha tocado tiene determinadas entradas y salidas y ciertas formas establecidas de comunicación con "el exterior" tengo que respetarlos. Dentro de él soy amo y señor, pero ésos son mis límites.

La modularización, el encapsulamiento de la funcionalidad, es lo que permite distribuir tareas en un desarrollo, y aunque parezca raro, es lo que permite que cada uno codifique a su manera al establecer límites para la creatividad, evitando que lo que uno haga rompa el código del otro.

Así enunciado, parece una regla social al estilo de "mis derechos terminan donde empiezan los de los demás" y algunos programadores cometen el error de interpretarlo de esa manera, como una cuestión de "urbanidad" que puede estirarse un poco. No es así. No es una regla social, y no puede estirarse. Si no trabajamos de esta manera, no funciona. Simplemente, nuestro software se romperá en mil pedazos al intentar integrar partes que no encajan. Una computadora integrando partes de un sistema no "estirará" sus reglas para hacer que el sistema funcione, así que debemos ser tan estrictos como ella al respetarlas.

La regla básica que se aplica a un programador es la misma que se aplica a su código: debe hacer lo que tiene que hacer, o dar un error. Pero nunca, **nunca** hacer otra cosa, que será una respuesta inesperada. Tenemos que respetar el diseño o preguntar, avisar, invocar una reunión, hablar con el diseñador, lo que corresponda en cada caso. Pero nunca, **nunca** hacerlo de otra manera.

Si el programador avisa, se tomarán decisiones. Que pueden ser del tipo "rompamos con todas las reglas y hagamos que esto funcione, después vemos". Pero en alguna medida se evaluará el impacto sobre otras áreas del sistema, y se guardarán precauciones (o no), pero la instancia de comunicación es imprescindible. El programador debe saber cuándo lo que hace **puede** afectar al resto del sistema, aunque no sepa en qué manera lo hará o si lo hará realmente. Esto lo vuelve confiable.

Y llegamos al nudo central, que veo que se viene repitiendo en varios post relacionados con equipos de desarrollo. La comunicación y la confianza.

Viniendo de un programador con cierta experiencia, una mala decisión de este tipo es un error grave, y es más grave si de alguna manera denota que no es consciente de cómo sus acciones afectan al sistema que desarrolla junto con otros. Este tipo de "malas decisiones" generan desconfianza sobre el trabajo que se le puede asignar a un colaborador, porque nos indican que no podemos estar seguros de que lo haga en una forma que sea útil desde el punto de vista del sistema completo. De nada sirve que el código funcione bien si no puede integrarse al resto del sistema.

El momento de las integraciones es uno de los momentos finales en el desarrollo y es en general un momento de nerviosismo, de apuro, donde si no estamos sobre la fecha es que ya se nos ha pasado. Es un **muy** mal momento para enterarse de que una parte importante del producto genera errores al ubicarse con las demás, porque para el "integrador", que ve a los módulos como piezas que deben encajar unas con otras, este tipo de errores genera una infinidad de problemas que no puede resolver porque no conoce el contenido de cada módulo. Y si el error es grave puede que no haya forma de "encajarlo" en los tiempos requeridos.

Es en este momento donde alguno de los responsables del conflicto dirá una de las siguientes frases:

  • "Es que no se podía hacer de otra manera. El diseño estaba mal." El momento de disparar la alarma era cuando no se podía codificar la funcionalidad. Ahora vemos que no fue válido hacerlo de otra manera, y que se ha malgastado el tiempo que debía haberse dedicado a corregir el diseño. Tiempo perdido que genera más tiempo perdido, ya que necesariamente hay que hacer una adaptación para poder seguir integrando.
  • "Pero esta forma es mucho mejor". Puede ser. Pero ahora tenemos una porción del sistema que no encaja con las demás. Está mucho mejor que las demás, pero no encaja. Supongamos que tengo 10 módulos y uno está "mejor". ¿Hay que ajustar los otros nueve? ¿Cuándo? ¿Con qué tiempos y presupuesto?
  • "El módulo funciona, yo terminé mi tarea a tiempo y correctamente". Esta es una variación de la conocida frase multiuso "En mi máquina funciona". Justamente estamos en la situación en la que vemos que el módulo no funciona en **este** sistema, y el hecho de que funcione en **otro** sistema (parecido a este) no es relevante.
  • "Es que no entendí. Yo pensé que el diseño decía esto." Esta es la única respuesta válida, si es sincera. Y revela no una falta del programador, sino probablemente del equipo en su conjunto. ¿Por qué se mal interpretó? ¿Era confuso? ¿Cómo no nos dimos cuenta antes? ¿Faltaron puntos de control? ¿Faltó hablar más entre nosotros?

5 comentarios:

rickhunter dijo...

Flaco me lees la mente.

Anónimo dijo...

Parece que todavía hay gente que sólo ve el desarrollo de software bajo el modelo de ciclo de vida en cascada, cuando este lleva siendo criticado decadas. El pensar que una vez que se tiene un diseño, este no puede ni debe ser modificado no permite que el software se adapte a los problemas que surjan durante su desarrollo.

Anónimo dijo...

Me parece que no has cogido el sentido real del post, lo que pretende decir es que en el caso de que en la fase de desarrollo se noten deficiencias en el diseño existen dos opciones:
1. Se mantiene el diseño por todos los medios aunque este mal.

2. Se reinicia la fase de diseño teniendo en cuenta las debilidades del anterior.

Lo que se pretende decir es que NUNCA se debe modificar el diseño de un sistema de forma unilateral si tener en cuenta al resto del equipo.

Anónimo dijo...

Sí, tienes razón, quizás no me haya centrado en la idea principal del artículo. La verdad es que la segunda mitad se centra más en la idea de nunca modificar un diseño de forma unilateral, con la que estoy totalmente de acuerdo. Pero en la primera parte veo que todo el artículo está basado en la premisa de que el software se desarrolla siguiendo el modelo de ciclo de vida en cascada. Trabajar en el diseño hasta que está "completo", una vez que se considera que se tiene el diseño "bueno" se acepta y se pasa a la fase de implementación, dónde ya no se puede tocar el diseño.

Hay varias frases que me indican esta asunción:


- Voy rumbeando hacia el diseño final, que era bastante elaborado, no por lo complejo sino justamente por su simpleza.Así que luego de un largo rato tenemos el diseño completo y pasamos a la codificación. Diseño "final", "completo" y luego pasar a codificación, como dice el modelo en cascada. Suponer que en ese punto se tiene un diseño final y completo sólo puede llevar a problemas del tipo que plantea el artículo.



- Recordemos el momento: ya habíamos cerrado el diseño hacía bastante tiempo. Eligieron la que cambiaba el diseño. Idea de diseño "cerrado". El diseño no se puede cerrar, el diseño tiene que poder ser cambiado para que el software pueda evolucionar.



- ¡¿Por qué eligieron esa?! Me rompieron el diseño Otra vez la idea de que el diseño es el final y el bueno, y modificarlo es "romperlo".



- Pero se establece [el diseño]. Y punto. Si no nos gusta, ése era el momento de dar pelea, y si la dimos y perdimos, perdimos. Luego taza taza cada uno a su casa y a codificar. Lo mismo de nuevo. La idea de la cascada de que una vez que se acaba la fase de diseño ese es el establecido y ya no hay vuelta atrás.



Creo que el artículo se centra en el problema del código que no sigue un diseño consensuado y los problemas que eso puede acarrear. Pero no mira en la raiz del problema ni en los motivos que pueden estar causando esas situaciones.

El autor asume desde el comienzo el modelo de ciclo de vida en cascada, y luego se centra en el problema de encontrarse, en las fases de implementación e integración, con un sistema que no sigue lo establecido en la fase de diseño y el coste que acarrea solventar esta situación. Pero hace todo eso sin plantearse que la raiz de ese problema puede estar en sus propias premisas, en el modelo que él mismo escoge, la cascada. En el modelo en cascada, los errores de cada fase se arrastran a las siguientes y cuanto más tarde se detectan estos errores más costoso es solventarlos.

AcP dijo...

Voy a aclarar, porque veo que oscurece.
Nunca asumí el modelo en cascada, ni lo propongo ni lo defiendo. Soy de la idea de que el desarrollo debe ser iterativo, y diría más, un gran simpatizante de Extreme Programming, como pueden ver en mis recomendaciones y otros artículos.
También es verdad que la redacción se presta a confusión, por ello esta aclaración.
Las frases que remarca el comentarista anterior deben entenderse dentro del marco de una iteración, de las que pueden darse varias por día e incluso varias por hora, cuando recién empezamos y no sabemos muy bien cómo viene la mano.
"Diseño final" es definitivamente confuso, espero mejor puntería para la próxima.
Cuando escribo "cerrado", lo digo en el sentido de "nos habíamos puesto de acuerdo". Una vez terminada la reunión del diseño, para mí esta "cerrado" y sólo puede retocarse en una nueva reunión (que puede ser tan informal como una charla de medio minuto).
Por lo de "taza, taza" creo que es así, a menos que hagamos XP programamos solos, e incluso uno de las recomendaciones menos adoptadas de XP es la programación de a pares.
Así que creo que los comentaristas y el autor (yo) están de acuerdo.
Como verán, ante una falla de diseño (en este caso de diseño de algunas frases del artículo) se comunica que hubo un "error" (mediante el comentario) y se procede a enmendar el diseño, que quedará así hasta que alguien avise lo contrario. Malo hubiese sido que el comentarista se quede con su aporte sin decir nada.
¡Gracias por los comentarios!