Mostrando entradas con la etiqueta Programacion. Mostrar todas las entradas
Mostrando entradas con la etiqueta Programacion. Mostrar todas las entradas

lunes, 20 de septiembre de 2010

"Pasos de Bebe", El fin del Debugging?

Baby Steps

Mirando a nuestro hijos aprender a caminar es fascinante. En un momento están agarrados de un sillón, una silla o de nuestras piernas y de repente comienzan a dar el primer paso, pequeño, muy cercano, y luego otro, tanteando con un pie hasta estar firme y luego , tambaleantes, llevan el otro pie al lado. Y así. Con el instinto que la madre naturaleza les dio, nunca intentan empezar a correr o dar zancadas.

Alla Lejos y hace tiempo

Hace muchos años, en nuestros primeros trabajos (en esa época teníamos que fabricar nuestros propios bytes :), nuestra forma de trabajo, a grandes rasgos, era algo asi:

1. Escribir un montón de código.
2. Compilar el código escrito y corregir los errores (no, no habia intellisense).
3. Probar y debuguear el código una y otra vez hasta que funcione.

Cada una de estas etapas tomaba días o incluso semanas. Cuando nos preguntaban como venía el proyecto, la respuesta típica era "ya terminé de escribir el código, me falta probarlo". Si tenías suerte, nadie te preguntaba cuanto tiempo de trabajo te faltaba...

El problema justamente es que luego de incluir en el proyecto una cantidad de lineas de código producida durante varios días, no hay forma de predecir cuanto tiempo llevará terminar el punto 3. Y ni siquiera estamos hablando de que este punto implicaba realizar "casos de prueba" exhaustivos, este punto solo significaba que lograbamos hacer que el código funcionara en los escenarios más comunes (con suerte).

Esta forma de encarar el desarrollo es un resultado natural de toda la corriente de Waterfall muy en boga en esos días. Si lo más productivo es primero hacer todo el análisis, después todo el diseño y después toda la codificación, tiene sentido dividir este último paso en etapas también.

Con el tiempo, y luego de muchos golpes, y algunas pesadillas, empezamos a trabajar de una forma bastante distinta:

1. Escribir 3 o 4 líneas de código.
2. Compilarlo.
3. Probarlo.

La gran diferencia es que generalmente los pasos 2 y 3 no llevan nada de trabajo, ya que somos capaces de escribir 3 líneas de código sin equivocarnos o cometiendo sólo errores obvios y luego probar que funcionan esas 3 o 4 lineas nuevas es algo manejable y en general muy sencillo de realizar(de hecho, programando así casi no se usa el debugger, vale la pena remarcarlo: Casi no se usa el Debugger, y ni les cuento si esas 4 lineas de código se escribieron a partir de un test, haciendo TDD).

Esta forma de trabajar fue descrita como una buena práctica de desarrollo por varios referentes de nuestra profesión, la mayoría proveniente de las metodologías ágiles, y se le dio el nombre de "Pasos de Bebe", "Baby Steps", en ingles. Recientemente se la describió con otro nombre mucho más técnico, "Desarrollo Nano Incremental".

Ahora, cuando nos preguntan como va el proyecto, generalmente la respuesta es del tipo "la aplicación ya puede hacer X e Y, falta que haga Z y terminamos". Además el tiempo total que lleva hacer las cosas es mucho menor, ya que los pasos 2 y 3 llevaban semanas (y a veces un tiempo indeterminado) en la forma de trabajo anterior, llamemosla, para ponerle un nombre igual de rimbombante: "Big Bang de Código".

¿Por qué se da esto?

En primer lugar, porque el desarrollo de software es complejo y nosotros los humanos no podemos manejar toda la complejidad de golpe si no la atacamos dividiendola en "trozos" manejables (Divide y Venceras, dice el dicho). Somos capaces de hacerlo bien para cambios pequeños, pero cuanto más grande es el salto que queremos dar, más probabilidades tenemos de cometer errores, que después llevan mucho tiempo corregir.
Esto se nota mucho cuando utilizamos TDD, donde escribir tests demasiado grandes nos lleva a cometer muchos errores e incluso produce que uno no pueda avanzar (esta fue una de las cosas que notamos en el último Code Retreat).

En segundo lugar, tomar pasos pequeños nos permite aprender de nuestros errores. El conocimiento que obtenemos en cada ciclo es utilizado en el siguiente, que tiene lugar unos minutos después. En cambio cuando se utiliza el "Big Bang de Código", las cosas que se aprenden se pueden utilizar recién en el siguiente proyecto.

Además, tener siempre código compilable y que funciona nos permite obtener feedback de la gente para la cual estamos trabajando. Es un hecho que la mayoría de los clientes no sabe exactamente lo que quiere hasta no ver una aplicación funcionando y si desaparecemos durante meses antes de mostrar algo es probable que terminemos programando algo muy distinto a lo que nuestros clientes necesitan.

Por último, este desarrollo nano-incremental permite justamente ir evolucionando muy despacio el diseño, y sólo con lo que se necesita para resolver el problema en cuestión sin agregar funcionalidad extra que no se necesitara. Justamente al diseño así obtenido se lo llama Diseño Incremental.

Pasen la Palabra

Pese a todos estas razones, y evidencia de muchas fuentes, y entre ellas, nuestra propia experiencia, vemos que la forma de trabajo más común entre los programadores sigue siendo el "big bang" de codigo. Y para ser sinceros, hemos tenido hasta ahora relativo éxito en conseguir convencer a nuestros compañeros de utilizar el "Pasos de Bebe".

Intuitivamente, se sigue creyendo que juntar grandes lotes de código para después probarlo es más eficiente y productivo. Es razonable, porque para muchas tareas de la vida real es así: si uno va a lavar los platos conviene primero ponerle detergente a todos los platos, después enjuagarlos y después lavarlos. No tiene sentido tomar un plato, ponerle detergente, enjuagarlo y secarlo.

Sin embargo, el desarrollo de software no se parece al lavado de platos (ni tampoco a la construcción de edificios para el caso).

Así que si se animan a probar un deporte de riesgo, si les gusta luchar con leones hambrientos y no son débiles de corazón, en su próximo proyecto apliquen esta practica de desarrollar en "Pasos de Bebe". Les aseguramos que van a ver una considerable mejora en su productividad.

Y mucho, mucho, menos debugging.

lunes, 2 de agosto de 2010

Code Smells

De chiquitos aprendemos que hay que evitar lo que tiene mal olor. La madre que huele el pañal de su hijo sabe que si apesta tiene que cambiarlo, y si apesta mucho, mucho, quizás el bebe tenga algún problemita de diarrea o algo similar. Sentido común universal este que sin embargo no aplicamos frecuentemente en el desarrollo de software.

A lo largo de la corta pero no menos exitosa vida de este Blog hemos mencionado varias veces a los Code Smells, sin aclarar su significado, mea culpa, porque a veces damos por sentado conceptos que son muy útiles. Este post entonces intenta corregir semejante falta:

Code Smells: (Aplicado al arte de la programación) son todos los síntomas que podemos encontrar en el código fuente de un sistema que indican que muy probablemente existan problemas más profundos de calidad de código, de diseño o de ambos.

El termino fue acuñado por Kent Beck y Ward Cunninghan hacia fines de los 90 y su uso se popularizó a partir del libro de Martin Fowler y otros: "Refactoring, Improving the Design of the existing Code" .

Si pensamos las barbaridades que hemos visto en algunos códigos fuentes (siempre de compañeros, nunca nuestros... :O ) y asociamos esa sensación de horror y rechazo con el olor a podrido de algunos ríos contaminados (Nota para los argentinos: Iba a poner Riachuelo pero no se si el resto del mundo conoce la fama "olorosa" de nuestro querido río, querido porque esta en la Boca ;), decía, esa imagen gráfica claramente lo que significa Code Smell, síntoma de que algo Apesta, y como al pañal, hay que cambiarlo.

En la práctica, históricamente, los Code Smells se han usado para identificar partes del código de baja calidad a las que aplicar las técnicas de refactoring explicadas en el libro arriba mencionado. Es decir, el libro describe un conjunto largo de distintas técnicas de transformación de código sin alterar su significado, las asi llamadas "Técnicas de Refactoting" y los Code Smells son los que indican en que parte del código hay que revisar como candidatas para aplicar esas técnicas.

No es la intención de este post describir uno a uno los Code Smells porque eso llevaría demasiada "tinta virtual" pero prometemos ir describiendo los Smells principales ya que consideramos (y bueno, no seria nada nosotros pero también lo piensa la gente más brillante de nuestro campo de la programación) que es uno de los conceptos fundamentales de la programación de estos tiempos, de los más aplicables y útiles, en combinación con los tests de unidad y que mayor impacto tienen tanto para la productividad como para la calidad de los sistemas que generemos.

Los principales Code Smells que se pueden encontrar en el código son :

  1. Código Duplicado
  2. Parámetros Booleanos
  3. Métodos Largos
  4. Clases Largas
  5. Larga Lista de Parámetros
  6. Cambio Divergente
  7. Shotgun Surgery (traducido algo asi como "cirugía a escopetazos")
  8. Feature Envy (textualmente: "envidia de features")
  9. Data Clumps (algo asi como aglomeracion de datos, masa de datos, en sintesis: un bodoque de datos (Arg))
  10. Obsesión por primitivos
  11. Generalización especulativa
  12. Campos temporales
  13. Refused Bequest ( algo asi como Rechazo a recibir parte de una herencia)
  14. Etc. (porque hay muchos mas).

Sigan sintonizando nuestro canal http://trainedchimpanzees.blogspot.com , porque vamos a estar analizando los mismos en sucesivos posts. De hecho ya empezamos a describir los Code Smells, uno de los primeros posts que hicimos fue sobre el Smell "Parametros Booleanos".

Para terminar una frase atribuida a la abuela de Beck, tómenla como consejo:

"Si apesta, hay que cambiarlo!"

lunes, 19 de julio de 2010

POO - Ejemplos de la ley de Demeter

Para dar un poco más de sustancia y aclaración al posteo del otro día sobre leyes y principios de Programacion Orientada a Objetos vamos a ir armando pequeños ejemplos de aplicación de los mismos.

Seguro no tenemos la menos necesidad de repetir porque Toooodoos recuerdan la ley de Demeter que decia:

 Un método m, de una clase C, solo debe invocar métodos:

  1. De la misma clase C (métodos privados)
  2. De un objeto creado en m (Variable local)
  3. De un parámetro, pasado como argumento a m (parámetros)
  4. De una variable de instancia de C. (Variables de instancia de la clase

En otras palabras, un objeto no debe conocer la estructura interna de los objetos directamente relacionados con él, ni obtener a través de ellos a otros objetos para manipularlos o enviarles mensajes.

Para ser no-innovadores vamos a usar un ejemplo clásico de la ley de Demeter, conocido como el ejemplo del chico-Diariero (PaperBoy). La clase en nuestro ejemplo la nombramos simplemente Diariero. El ejemplo se basa en la relación entre El Diariero y el Cliente del diario en el momento en que se quiere cobrar tan solo un diario entregado.

Una posible implementación que viola el principio de Demeter (y que la hemos visto utilizada en código de proyectos reales, con ligeras variaciones, una y otra vez) es :

 
 
public class Diariero 
{
private static final 
double COSTO_DIARIO = 3.5;
 
  public Money cobrarDiario(Cliente cliente) {
    return cliente.getBilletera().extraer(COSTO_DIARIO);
  }
}

Es decir, la clase Diariero usa al parametro Cliente, le pide su billetera y extrae de ahi directamente el costo del Diario (el cual, para simplificar aún mas es una constante).

Aqui se puede ver fácilmente porque se viola el principio de Demeter de una manera bastante gráfica. El diariero está literalmente "metiendole la mano en el bolsillo" al cliente, y sacandole la plata. De una manera coloquial a esto es exactamente lo que apunta a evitar el principio. Que los objetos no "violen" los limites de las responsabilidades asignadas ni generen acoplamientos innecesarios. Que pasaría en este ejemplo si el cliente nos quiege pagar con algún otro medio de pago, por ejemplo, con Cheques? Habría que modificar ambas clases, Cliente y Diariero (y quizás algunas más).

Es interesante ver que el uso de getters tipo getBilletera es muy común y no causa sospechas en nadie (de hecho muchas ides tienen forma de generarlos automáticamente), pero que si en vez de usar un getter usáramos variables de instancia públicas tendríamos inmediatamente a la policía de los Objetos pidiendo nuestra captura. Sin embargo, el acoplamiento causado por este uso de los getters es casi el mismo que si usáramos variables públicas (y ni hablar cuando no solo tenemos getters sino que también tenemos setters!).

Veamos por otro lado una implementación que respeta la ley de Demeter :

 
public class Diariero {
   private static final double  COSTO_DIARIO = 3.5;
 
   public Money cobrarDiario(Cliente cliente) {
     return cliente.cobrar(COSTO_DIARIO);
   }
}
 
public class Cliente {
   private Billetera billetera;
 
  private Billetera getBilletera() {
     return billetera;
  }
 
  public Money cobrar(double costoDiario) {
      return getBilletera().extraer(costoDiario);
  }
 
}

Notar que en este ejemplo, el método getBilletera permanece privado (es algo privado justamente de cada Cliente), y no es accedido directamente. El diariero le pide al cliente que le dé el dinero a través del método cobrar. Luego este método es el que resuelve como obtener el dinero, por ahora extrayéndolo de la billetera, pero si quisiera podria devolver un cheque (deberia ser una subclase de Money, quizás) o algún otro medio de pago, sin necesidad de modificar la lógica de la clase Diariero.

En general se debe seguir la Ley de Demeter pero hay que tener cuidado con hacerlo demasiado al pie de la letra. Demeter apunta a reducir las llamadas del estilo a.b().c().d() (los clásicos "choques de trenes") , que claramente indican un problema, pero también hay que tener cuidado con llenar las clases de métodos que solo funcionan como intermediarios entre objetos. Al respecto Martin Fowler dice que estaría más correcto llamar a esta regla la Sugestion de Demeter, ya que no es algo absoluto.