lunes, 7 de julio de 2014

Code Smells - Métodos Largos




Todos conocemos la máxima "Si bueno y breve, dos veces bueno" verdad?
Nunca es mas cierto esto que en el desarrollo de software.


Los Metodos Largos son Malvados

Hace mucho tiempo, incluso antes del surgimiento del paradigma de la Orientación a Objetos (POO), que se sabe que las funciones, procedimientos y  subrutinas son mucho mas mantenibles, tienen menos problemas y errores y sobreviven mucho más si son cortos.

En realidad la teoría de Calidad de software habla de cohesión funcional cuando todo el código del elemento al que se referencia (modulo, método, clase o función) se ocupa de implementar una y sola una función bien específica. Es decir, realizan una sola y bien definida tarea. Y para implementar esa única y bien definida tarea no se necesitan muchas lineas de código.

La POO real esta basada en una serie de intercambios (envío y recepción) de mensajes claros y bien definidos entre objetos pequeños y con un propósito especifico (ver el Principio de Responsabilidad Unica, SRP, en este mismo blog). Y la inversa en este caso es totalmente cierta, sin un código hace varias cosas a la vez, tiene varios propósitos, genera mas de un efecto a la vez, o  hace dos o mas cosas con mas de un objetivo , entonces, por lo general eso ocupara muchas lineas de código, generando asi metodos largos.  Los Métodos largos representan uno de los Code Smells más claros respecto a marcar código... que apesta (En Inglés: APESTA!).

A riesgo de caer en la obviedad vamos a preguntarnos:

Motivos por los que los metodos largos...apestan ?

Para empezar, cuanto más largo es un método es mucho mas difícil de entender en su totalidad y  de comprender los efectos colaterales que genera. Como sabemos, esto tiene relación directa con la mantenibilidad, porque cuanto mas difícil es comprender un código mas riesgo hay de que al modificarlo se introduzcan errores.

En segundo lugar un método largo casi con seguridad no posee cohesión funcional.  Inclusive podemos apostar sin temor a equivocarnos que tendrá el peor tipo de cohesión que hay: coincidental. Es decir el código hace varias cosas, muchas no relacionadas mas que por la coincidencia, lo cual aumenta a su vez la probabilidad de tener que tocarlo durante la vida de mantenimiento del sistema (que como dijimos alguna vez...es el 80% al 90% del tiempo de vida de un sistema, siendo el 10% restante el tiempo original utilizado para su desarrollo inicial).

Pero en ningún otro lado afecta la longitud de un método tanto como en el atributo de su testeabilidad, es decir, cuan sencillo/factible es testearlo, y ahi mis amigos, es cuando los agarro sin escapatoria.

Un método largo es imposible de testear correctamente, porque simplemente, hace DEMASIADAS COSAS, y entonces hay que testear todas esas cosas y peor que peor, la diferente combinación de ellas. Esto produce rápidamente un efecto de multiplicación geométrica y entonces, los tests, cuando los hay , tambien son complejos y largos. Entonces perdemos por los dos lados, porque encima el código quedara con bajo cubrimiento de tests o ninguno, y si hay tests sera una pesadilla manternerlos.


Como Solucionarlo

Aquí van una lista simple de algunas heuristicas a aplicar, no son infalibles ni, aclaro, los absuelven, queridos chimpancés entrenados,  de utilizar el sentido común :

  1. En la mayoría de los casos alcanza con descomponer el método largo con varios métodos cortos, refactorizando utilizando el refactor "Extract Method". La forma más simple que funciona es ir recorriendo el método de arriba a abajo, encontrando 4 o 5 lineas de código que realizan una unica función claramente definida  y extraerla como un método nuevo llamado desde el método largo.
  2. Si el método tiene además largas listas de parámetros es una indicación de que sufre de 2 o más Code Smells a la vez (lo mas común), para lo cual es una buena práctica primero reemplazar los parámetros relacionados por un Objeto que los contenga (una Clase generalmente nueva que pide, "a gritos!" ser creada) y recién ahí aplicar el punto 1.
  3. Candidatos a extraer métodos son las líneas intermedias de un bloque IF y por separado, en otro método, en general, las líneas del bloque else.
  4. En Java, por ejemplo, o lenguajes con manejo de excepciones existe el típico caso de bloques (encima Java con su azúcar sintáctica (Syntactic sugar) lo hace más largo todavía!): 
     try {
           hacerA()
           hacerB()
           hacerC()
      } Catch (AgarrateException A) {
      }  Catch (AgarrateOtraException B) {
      } Catch (AgarrateTodas C) { }

En este caso ya es bastante mala la sintaxis del lenguage para que encima le agreguemos fuego al incendio. Un buen remedio, en general , es reemplazar "hacer A", "hacer B" y "hacer C" por un extract method, "metodoConEfectoABC" : try { metodoConEfectoABC() } catch (){ ...

     5. En cada paso, agregue un pequeño test de unidad que testee el nuevo metodo extraido.



Concluyendo porque los artículos largos también son malvados.

Este es en realidad uno de los code smell más común con que nos encontramos. Si hubiera que elegir un y solo un code smell para atacar para lograr mejorar lo máximo posible con un solo tiro la mantenibilidad del código, este es el que elegiría. Por supuesto que no alcanza con el para lograr tener una mantenibilidad aceptable o buena calidad, es solo un  buen comienzo  hacia el lado correcto.

Es muy común, luego de refactorizar uno de estos métodos largos en varios mas pequeños  que  varios de los nuevos metodos se parecen o realizan funciones similares a otros metodos ya existentes encontrándonos asi antes uno de los mas peligrosos code smell: Duplication is Evil.

Así que, porque se que se lo estaban preguntando, la tarea de un profesional del Código Limpio nunca termina y debe seguir la maxima de los Boys Scouts: Siempre  Listo....para mejorar la calidad del código.





martes, 29 de abril de 2014

A las metodologias agiles se las comio el sistema I




Si Señores y Señoras (para ser politicamente incorrectos) aqui estamos nuevamente despues de un nuevo impasse en escribir este blog y antes del siguiente impasse cuando dejaremos de escribir en este blog. El show debe continuar, que no se corte, asi que aquí vamos.


Hace poco en foro-agiles, el foro sobre metodologías agiles de la comunidad hispanoparlante, hubo una discusión sobre la corrupción de Agiles como concepto, originada en  sendos articulos de Uncle Bob Martin, The True Corruption of Agile, y Dave Thomas, Agile is dead.

A raiz de eso @gabrielMorrisS escribio un interesante post Corrupcion o acomodacion Agil del cual rescato la siguiente frase:

"No estoy diciendo que la corriente ágil sea la verdad absoluta, de hecho no
lo es, pero si creo que se debe ser consecuente con los principios que se
recitan a viva voz. No creo en fanatismos desbordados pero tampoco en la
acomodación relativa de las cosas por simple conveniencia."



En mi opinión, el problema de la corrupción de agiles no tiene tanto que ver con la proliferación de cursos solo focalizados en practicas soft  o los cursos que se basan solo en como gestionar proyectos agiles, o en temas humanos y de comunicación. Aunque claro,  en uno de estos cursos, como parte del mismo  hacían olerse las manos unos a otros, en otro te hacían representar películas con tus cuerpos...okay  quizás si esas practicas sean demasiado _________,  no se como nombrarlas.

Es decir, estas practicas (y principios y valores) bien dictados y por gente capaz y honesta cson interesantes y útiles y tan solo por la simple imposición de un proceso ordenado y simple (valga la redundancia) con foco en la comunicación y en las personas generan una gran mejora inicial respecto al caos anterior. 

Pero el problema es cuando es lo único que se enseña y aplica porque entonces la mejora no se sostiene a largo plazo.

Y lo que veo que pasa habitualmente es lo siguiente:

Una empresa  quiere  empezar a aplicar metodologías ágiles va y entonces contrata precisamente uno de
esos cursos. Supongamos que el curso esta bien dado, todos tienen los rudimientos de un proceso como Scrum y terminan todos muy contentos. Listo.
Respecto a la  parte técnica, bueno , claro, eso  ya vendrá mas tarde, todavía no estamos maduros, la gente tecnica ya sabe lo que tiene que hacer, solo aplicando scrum nos alcanza, etc etc , pongan aqui la excusa que quieran pero lo que sucede es que la empresa empieza a aplicar Scrum sin aprender, sin practicar, sin EXIGIR, ningun cambio en como se escribe el codigo sin intentar siquiera aplicar las mal llamadas practicas hard, las practicas mayoritariamente tecnicas como  TDD, pair programming, refactoring, clean code, continous integration, etc.

Lo cierto es que aprender la parte tecnica, las 13 practicas originales (surgidas de XP) es mucho mas difícil y trabajoso,  y conlleva mucho mas tiempo y esfuerzo que las practicas soft. Los gerentes y/o PMs no las entienden ni entienden cuan imprescindibles son, y cuando si lo hacen de cualquier manera el  tiempo apremia, los deadlines se acercan, y se corta el hilo por lo mas hard (quiero decir, lo mas delgado :P , por lo técnico). 

Entonces, la tipica implementacion de agiles que veo, una y otra vez es la siguiente:

1 - Los que eran Lideres o Project Managers antes ahora se llaman Scrum masters. Los Usuarios clave ahora son Product Owners.

2 - Hacemos Sprints de una semana o dos o tres o tres meses, da lo mismo, total, lo importante es hacer iteraciones.

3 - Se hará alguna que otra reunión a la que llamaremos planning meeting y alguna otra cada dos o tres dias que llamaremos Daily (?).

4- Hacemos reunion al final de cada sprint o proyecto, para festejar o echarnos la culpa a todos, las llaman "retrospectivas"


Y es verdad que la sensacion dentro del equipo y hasta para el usuario es la de ir mas rapido. Y el foco en las historias mas importantes hace que se vea mas valor agregado. Y al ir pasando los sprints por lo menos los usuarios se ven mas involucrados, se los consulta. Y todo esto es fantastico, estamos llendo ahora en un auto cada vez mas rapido a 150 km por hora hasta que....
Hasta que empieza a acumularse codigo mal hecho, a las apuradas ("ey, ni siquiera hay tiempo de testear demasiado, lo haremos al final"), comienzan a aparecer presiones (estas historias tienen que estar terminadas para el fin de este sprint). Y el codigo comienza a acumular deuda tecnica, rapido porque la corrupcion del codigo crece siempre de manera exponencial y antes de que nos demos cuenta.....el auto comienza a vibrar, cuesta cada vez mantenerlo en la ruta deseada y luego de un par de sprint mas  estrellamos el proyecto contra el arbol del codigo inmantenible (lo confieso, la metafora me quedo algo forzada). 

Hay un sintoma que no falla: cuando comienza a haber modulos que no tocamos por miedo, cuando un simple cambio produce regresiones importantes, ya sabemos como sigue,y  lo peor, ya sabemos como termina esa pelicula (los buenos mueren).

Asi se terminan usando *solo* las practicas soft de agiles (y en la palabra "solo" reside el problema) y entonces los miembros de los equipos, usuarios y clientes terminan pensando  que  agiles es solo eso. Y esto es tremandamente peligroso porque no solo fracasa un proyecto sino que el mismo concepto agil se ve manchado, como dice Uncle Bob, corrompido.


 Asi en lugar de una verdadera metodologia agil, tenemos una especie de Cargo Cultismo Agile ( http://en.wikipedia.org/wiki/Cargo_cult o aqui mismo), honran todas las ceremonias del culto,  tocan tambores, construyen torres de control de madera pero.... los aviones no llegan.