Mostrando entradas con la etiqueta buenas practicas. Mostrar todas las entradas
Mostrando entradas con la etiqueta buenas practicas. Mostrar todas las entradas

miércoles, 20 de abril de 2011

Lecciones reaprendidas

Hoy tuve unas de esas peleas con el código que te hacen reconsiderar el pasarte a Management :). No fue muy sangrienta, sólo un bloqueo de media hora, pero cometí tantos errores repetidos que cuando finalmente logré hacer lo que quería no sentí satisfacción sino bronca.

Mi objetivo era hacer una subclase de una clase básica del entorno, para agregarle cierta funcionalidad. Estaba apurado porque tenía poco tiempo y quería terminar esta tarea hoy. Intenté el camino obvio: crear la subclase y utilizarla en lugar de la clase del sistema. Esto no me funcionó y como estoy en un entorno bastante nuevo (Objective-J/Cappuccino), decidí que la herencia no debía estar funcionando bien (??). Entonces me puse a buscar maneras de lograr mi objetivo de otra manera: intenté con un wrapper de la clase que quería modificar, con modificar el comportamiento de la clase que llamaba a la nueva clase y con unas cuantas maneras más, sin resultado. Busqué un rato en Google, pero no encontré ninguna referencia a mi problema. Estaba atascado. Ya perdido, volví a intentar el camino inicial, y funcionó!!. Revisé lo que había hecho en mi primer intento y por supuesto, encontré un problema en mi implementación inicial.

Los errores que cometí fueron los siguientes:

1. Asumir que la herencia no funcionaba. El compilador nunca está roto!!! Bueno, si, a veces está roto, pero en 20 años de programar sólo he descubierto 2 o 3 de esos errores y me he equivocado muchas veces. Es siempre mucho más probable que el error este entre el teclado y la silla.

2. Trabajar apurado. Quería terminar hoy y dejé que el apuro dominara mi forma de trabajar. Al final me llevó más tiempo que si lo hubiera hecho tranquilo.

3. No hacer un experimento. Si sospechaba que la herencia estaba andando mal, tendría que haber armado una clase sencilla y probar las cosas ahí. Si no lograba reproducir el problema, probablemente hubiera sospechado la verdad...

4. Insistir en arreglar el código que había hecho. Cuando uno no encuentra un error que obviamente tiene que estar ahí, lo más sencillo es borrar las modificaciones y empezar de nuevo. Tengo la costumbre de hacer commits muy frecuentes, así que volver atrás mis cambios no hubiera sido costoso. Además, estoy usando git, asi que también hubiera podido volver atrás temporariamente con un git stash.

5. Trabajar sólo. Este fue exactamente el tipo de problema que haciendo Pair Programming no me hubiera pasado.

Bueno, ahora que lo escribí se me pasó un poco la bronca... la próxima vez que sospeche del compilador espero acordarme...

jueves, 10 de marzo de 2011

Calidad del desarrollo I - El Pato de la boda

Volviendo de un periodo de inactividad en este blog a causa de vacaciones varias, algún que otro viaje y temas de trabajo de variada indole vamos a retomar nuestra habitual lucha entre bits, bytes, tuits, posts y lo que toque en suerte.

Hay un tema que siempre ha sido muy caro (por querido, no por el costo, aunque tiene que ver) a mis sentidos y es el tema de la Calidad de un desarrollo de software.

El tema, específicamente para este post es el siguiente:

Cuando comienza un proyecto somos todos buenos, las mejores intenciones son puestas en la mesa, y las buzzwords y palabritas fashion del momento vibran en el aire. Se escucha a programadores, funcionales, testers y lideres decir frases como por ejemplo:

"Vamos a trabajar con OOP bajo arquitectura SOAP y BRMS" "Utilizaremos CEP, BDD, MDD y TDD.", "Aplicaremos los mejores Design Patterns, el GOF, AOP y la madre que me pario."

y así en esas primeras reuniones todo es redondo y bonito, el papel lo soporta todo, así que el diseño de alto nivel cierra y reunidos ante la mesa redonda todos juramos proteger la Calidad del desarrollo cueste lo que cueste, verdad?

Bueno ahi entran a tallar el circulo de presiones de las 4 variables de todo proyecto (de sistemas por lo menos, que es lo que uno ha trajinado bastante):

Como todos sabemos las decisiones y acciones que realizamos durante un proyecto afectan constantemente a estas cuatro variables que están interrelacionadas y uno no puede manejar las cuatro a la vez (y eso de poder definir siquiera dos ya es de por si bastante difícil). Simplificando bastante (y lo que sigue si hilamos fino ni siquiera es totalmente cierto) o bien definimos el costo, alcance y la calidad y eso hace que el tiempo no pueda fijarse y será el tiempo que tome realizar ese alcance con ese costo y calidad. O bien definimos el alcance, tiempo y costo, y la calidad será la calidad que se pueda obtener con ese alcance, tiempo y costo.

Entonces, lo que sucede habitualmente es que un proyecto comienza con un alcance dado, un tiempo mas o menos definido de finalizacion, un costo fijo y determinado hasta los centavos y la promesa de una calidad altísima. Si, por si no se han dado cuenta empezamos casi queriendo violar las leyes de la física y manejar las 4 variables!

A continuación sucede algo conocido como "la realidad". Es decir, el alcance cambia. Siempre. De verdad. Siempre.

O bien el usuario se da cuenta que lo que pidió no es lo que quería, o se da cuenta que le falto pedir funcionalidad im-pres-cin-di-ble, o lo que era un simple linea en un documento termina siendo una funcionalidad clave cuyo desarrollo implica 3 módulos nuevos o bien el negocio o las leyes cambian o lo que sea pero lo cierto es que cambia. De cuando se de cuenta de esto depende en parte de la metodología de desarrollo que se utilice, no quiero entrar en detalle pero digamos simplemente que la tradicional en cascada suele hacer que el usuario se entere cuando ya no puede cambiar nada.

Uno puede tomar ante esta realidad dos caminos. O bien congela el alcance rechazando los cambios o bien permite los cambios. No vamos a discutir aquí que pasa si uno rechaza los cambios obligando al usuario a aceptar que desarrollemos un sistema que no necesita.

Al permitir los cambios sabemos que como el alcance ahora es mayor las otras variables se verán afectadas. El tiempo para terminarlas crecerá , saldrá mas caro terminarlo o..... o la calidad del producto sufrirá.

El costo en general es casi un taboo tocarlo. Implica decisiones políticas. Implica que el gerente del proyecto hable con el cliente o el gerente interesado (el que deberá "pagar") y blanquee la situación para asumir un cambio en el presupuesto. Todo lo cual es....complicado.

El tiempo para hacerlo también es parte de los "Intocables". En reglas generales hay deadline y fechas comprometidas dentro de los proyectos que implican determinadas promesas a clientes o sectores externos, promesas que de no cumplirse pueden traer problemas. En otras casos las fechas sirven como puntos de coordinación con otros eventos (lanzamiento de productos, de campañas de marketing, de instalación y capacitación para el nuevo sistema, etc.) que hacen que sea oneroso o muy impactante su prorroga.

Ni que hablar del costo.

El pato de la boda termina siendo siempre siempre la calidad.

El problema en este caso, material de otro post que publicaremos posteriormente, es que es, para seguir con las metáforas de patos y cazadores, como dispararse en el propio pie.

Cuando uno afecta a la calidad afecta fundamentalmente(el detalle, como dije, para un post próximo) a la velocidad para cambiar el sistema, a su estabilidad y mantenibilidad, es decir afecta en una espiral ascendente al tiempo y al costo.

Es decir que....porque cambio el alcance o nos estamos atrasando, para tratar de cumplir con la fecha y el presupuesto inicial dejamos de lado justamente la Calidad lo cual hace que vayamos MAS LENTO y terminamos no respetando ni el tiempo ni el costo. Y para colmo nos queda un sistema inmantenible!!.

Y ni que hablar que todavía no definimos que entendemos exactamente por calidad de desarrollo! Otra tarea para el próximo post!.





Nota de autor: "Ser el pato de la boda" es una expresión utilizada al menos en Argentina refiriéndose a alguien que carga con las responsabilidad de algún suceso sin ser el culpable y que debe pagar por ello.

lunes, 27 de diciembre de 2010

Editorial de fin del año del 2010 - De vuelta a las bases

Henos aquí al final del año 2010. Primer fin de año de este blog que esperamos que sea el primero de muchos.

La verdad, en estos casi 9 meses de vida (chiste obvio al margen) nos hemos dado algunos gustos. Como dicen por ahi, justamente los gustos hay que darselos en vida y así lo hicimos.

Recuerdo cuando era chico miraba series de televisión (y aquí voy a delatar nuestra edad, ey, tengan en cuenta que la mitad de nosotros somos treintañeros todavia!! mas respeto!) como los angeles de charlie, los dukes de hazzard, BJ (la del camionero con el mono), el Sheriff Lobo (imperdible), el hombre de la Atlantida (ju juuu por DioS!), buck roger o el auto fantástico. Cuando terminaba la temporada de estas series era habitual que el ultimo capitulo fuera en realidad una selección de las mejores partes de los capítulos que se habían emitido este año. Va aquí nuestro homenaje a esta práctica, como modo de resumen del año 2010.

Hemos repasado algunos principios básicos de objetos en la-ley-de-demetrio-y-otras-yerbas-oo y en poo-ejemplos-de-la-ley-de-demeter. No quiero olvidarme tampoco de la seria sobre patrones de tests de unidad: patterns-de-tests-de-unidad-1-como-deberian-ser-los-tests-de-unidad, patrones-de-test-de-unidad-2-humble-objects o como-lograr-tests-de-unidad-rapidos.

Estos principios y patrones parecen bastante obvios y trillados(bah, así nos parecían) pero charlando con mi amigo y co-autor de Blog nos hemos dado cuenta que en estos tiempos muchas veces debido a la falta de recursos para programación o porque hay otros temas mas "fashions", lo cierto es que muchos programadores nuevos no conocen ni a estos ni a los principios más básicos de la Programación Orientada a Objetos.

Hemos recién empezado a charlar sobre un tema muy interesante, importante como buena practica pero bastante extenso como son los Code Smells en code-smells, desgranando algunos en detalle como: desconfien-de-los-parametros-booleanos, o obsesion-primitiva y comentarios

Emprendimos una batalla, desigual desde ya, tratando de aclarar cuestiones muchas veces malentendidas o mal interpretadas de las metódologías ágiles, como en con-scrum-no-alcanza o estas-realmente-haciendo-desarrollo-Agil y dos en particular que me gustaron mucho, por la repercusión que tuvieron y porque fueron generadas con discusión dentro de la comunidad ágil iberoamericana: metaforas-del-desarrollo-de-software-i y metaforas-del-desarrollo-de-software-ii. Para terminar con el articulo fundamental de como encarar el aprendizaje de cualquier nuevo conocimiento y en particular de una metodología ágil : el-camino-del-conocimiento-de-aprendiz-a-maestro.

Como niños buenos e hijos de la generación "La inmaginacion al poder", hemos discutido como debe ser un buen líder y otras cuestiones de liderazgo en liderazgo-i y liderazgo-ii. Pero por otra parte, cuestionando nuestra conciencia, nos hemos encargado de recordar las obligaciones que tenemos como verdaderos profesionales de formarnos constantemente: cuantos-libros-tecnicos-leiste-este-año, y trabajar sobre lo que tienen importancia como en mantenibilidad-vs-reusabilidad, por-que-funciona-pair-programming y nos hemos auto-retado cuando perdemos el tiempo como en larga-el-twitte o productividad-la-espartana.

Para finalizar queremos resaltar dos articulos en particular, ambos han tenido mucha difusión, y creemos saber, al menos parte de, la causa: El primero es flexibilidad-ante-el-cambio que habla sobre porque hay que estar preparados para el cambio en todo proyecto de desarrollo, sobre como descubren lo que quieren los usuarios y como el cambio es esencialmente imparable porque es sencillamente parte de la realidad. Y el otro es pasos-de-bebe-el-fin-del-debugging, una de las herramientas menos utilizadas, difundidas y comprendidas de la programación pero que nosotros le asignamos una importancia capital para enfrentar el caos de la complejidad que impone El Cambio sobre el desarrollo de software, junto, claro está con las prácticas de TDD y pair programming.

Para cerrar el balance , además de los artículos, este año organizamos dos Code Retreats, que nos permitieron compartir tiempo con otros locos (perdón, quiero decir: con otros entusiastas de estos temas) capaces de juntarse un sábado a las ocho de la mañana a programar de a pares, resolver problemas y razonar sobre como mejorar nuestro arte.

Bueno, eso es todo. Lo que hemos tratado humildemente de hacer este primer año desde este espacio es describir y promover conceptos, libros, técnicas y herramientas que consideramos poco habituales dentro del ámbito de la programación.

Creemos que la terrible presión de la ola de información junto con la inundación de nuevos lenguajes, herramientas y frameworks impone tal ritmo al programador profesional que arrasa con cuestiones de base y produce que todo se confunda en un lodazal de conocimientos superficiales y la Ultima-bala-de-plata-que-viene-a-solucionar-todos-tus-problemas.

Lo que se necesita en realidad es adquirir buenos hábitos, aplicar Buenas Practicas de desarrollo y volver a las bases.

Por eso brindamos. Salud!.





La foto del brindis es propiedad de larrazun. Algunos derechos reservados.

miércoles, 1 de diciembre de 2010

Sherlock Holmes

"Una vez descartado lo imposible, lo que queda, por improbable que parezca, debe ser la verdad"

Esta frase fue escrita por Arthur Conan Doyle para su personaje Sherlock Holmes y resume el mecanismo de las investigaciones en la mayoría de las novelas policiales: ir descartando posibles explicaciones hasta llegar a la única posible.
Lo difícil, tanto en los casos de Sherlock como en la vida real, es poder descartar N-1 explicaciones, porque N es grande y porque descartar cada explicación no es fácil (creo que no lo podría haber dicho de una manera más nerd o geek, como se dice ahora).

Pero nosotros no necesitamos resolver cosas en la vida real. Sólo necesitamos que nuestros programas anden y existen factores que hacen viable comportarnos como si fuéramos Sherlock Holmes (o el Dr. House, para un ejemplo más moderno).

En primer lugar, las computadoras son determinísticas. Esto significa que si una computadora recibe dos veces exactamente el mismo input el resultado que va a producir va a ser exactamente el mismo. Esta idea no se condice con nuestra experiencia de todos los días (sobre todo si somos usuarios de ciertos sistemas operativos), pero no deja de ser una verdad absoluta, por lo menos para computadoras de nuestra época que no planeen tomar el mundo.

¿Cual es la consecuencia para nuestro método Sherlockiano? Que podemos descartar todas las hipótesis que sean del estilo "no cambié nada y dejo de andar". Si dejó de andar, es por un motivo; quizás nunca podamos descubrirlo; quizás lo haya hecho otra persona, pero existió un motivo (siempre quise poner un punto y coma escribiendo en castellano, es raro porque odio hacerlo cuando programo).

Este es un punto muy importante, porque nos permite liberar nuestro cerebro de mucho trabajo inútil. Si algo deja de funcionar, solo necesitamos ver que cosas cambiaron desde la última vez que anduvo y podemos estar seguros que alguno de esos cambios va a ser el culpable. Teniendo pocos sospechosos, siempre es más fácil descubrir al criminal.

Es aquí donde, además de las características inherentes de nuestro medio, entran en juego hábitos que permiten mantener la lista de sospechosos corta. En el párrafo anterior hay una frase tramposa: solo necesitamos ver que cosas cambiaron desde la última vez que anduvo. Parece sencillo, pero para saber esto se necesita poder determinar primero cual fue la última vez que nuestro programa anduvo y segundo que cosas cambiaron desde entonces.

¿Cómo podemos ser capaces de saber cuándo fue la última vez que nuestro programa funcionó? Si se trata de un problema que aparece en nuestro ambiente de desarrollo, las técnicas que mencionamos aquí son muy eficientes. Si dejamos pasar poco tiempo entre pruebas, es facil saber cuando fue que nos equivocamos. Los tests de unidad en estilo TDD son particularmente eficaces: si corremos los tests cada 10 minutos, un test fallado debe tener un sospechoso bastante obvio.

Si se trata de un problema que apareció en producción o en testing la cosa es más complicada. Hay que recrear los ambientes anteriores para poder verificar en que momento apareció el bug, lo cual a veces es difícil y consume mucho tiempo. Pero nada de esto es nuevo, siempre supimos que dejar llegar bugs a producción es muy caro.

La otra mitad del problema es determinar que cambió para que nuestro programa dejara de andar y aquí entra en acción un hábito muy querido por los autores de este blog: los commits frecuentes. Digamos que logramos determinar que el programa dejó de andar la noche del 24 de julio a las 3AM y que podemos considerar todos los cambios ocurridos cerca de ese momento como sospechosos. Si un programador estuvo modificando código sin commitear durante un mes e hizo commit a las 2AM, todos esos cientos de cambios son igualmente sospechosos. En cambio, si trabajamos de manera de poder commitear todos los días, el conjunto de sospechosos (la cantidad del codigo commiteado) es mucho más corto.

La misma solución se puede aplicar para los problemas en Producción, si no para evitarlos por lo menos para minimizarlos, aplicando las práctica de Integración Contínua y Entregas Frecuentes. La idea es la misma: No esperar a tener mil cambios en la aplicación para pasarla a productivo porque eso implica que cualquier error tiene miles de "sospechosos". Mientras que si uno hace mini-implementaciones y entrega frecuentemente, cada bug tendrá unos pocos "posibles" sospechosos.

Por supuesto, hay variaciones de estos problemas en equipos de trabajos grandes, con gente que modifica a la vez las mismas partes del programa, pero la regla general se mantiene: si algo dejo de andar fue por algún cambio y cuando más fácilmente puedas determinar ese cambio tu vida va a ser más fácil.

Elemental Watson!, Diria Sherlock Holmes (Claro que esa frase no la dice en ninguno de los cuentos o novelas que escribió Arthur Conan Doyle, "Urban Legend" que le dicen).

miércoles, 6 de octubre de 2010

Code Smell - Codigo Duplicado

Queridos Chimpances entrenados, Recuerdan lo que eran los Code Smells?

Bien, si tuvieramos que entregar el Oscar a los Code Smells, el número uno, el primer premio se lo lleva sin dudarlo el código duplicado.

Codigo duplicado implica tener la lógica del sistema duplicada en dos o mas lados, en la misma o distintas clases, incluso en distintas capas.

Porque esto es malo?

El primer problema es que cuando tengamos que modificar el código duplicado (y SI,antes de que pregunten les digo: Vamos a tener que modificarlo con 100% de seguridad) es muy probable que nos olvidemos de cambiar alguna ocurrencia de la duplicación, introduciendo bugs.
En segundo lugar estamos salteando una relación de ese código con alguna de las clases. Es decir, a alguna clase realmente le corresponde ese comportamiento que está duplicado y en el resto de los lugares de la duplicación no estamos honrando está relación que mas pronto que temprano nos traerá problemas.
Es fundamentalmente en este sentido en que es un Code Smells de nuestro diseño. Algo malo esta pasando, falta un método y ese método debe ir en alguna clase lo cual causará un rediseño de nuestro sistema.
En tercer lugar el código duplicado produce que el código sea más dificil de leer y de entender.

Además, el código duplicado tiende a ser modificado de maneras ligeramente distintas en cada una de las copias, con lo que suele pasar que después de un tiempo ya no es trivial decir si dos fragmentos de código que originalmente eran duplicados siguen haciendo lo mismo o no.

En sintesis, el problema fundamental de tener la lógica duplicada es que hace al sistema más dificil de mantener, y ya sabemos que la mantenibilidad es la propiedad numero #1 que debemos procurar para nuestro codigo.

Ahora bien, la lógica duplicada a menudo se presenta en formas mucho más sutiles que una duplicacion literal de codigo, si bien hemos visto más de un ejemplo de estos.

Podemos encontrar los siguientes tipos (principales) de duplicacion de código:

1 - Duplicacion simple: Es la forma más común de duplicacion. Sucede cuando se tiene la misma expresión (identica o diferentes en variables) en dos métodos de la misma clase. La solución es aplicar el refactoring "Extract Method" extrayendo el código duplicado en un método privado y llamando desde ambos lugares de la duplicación al nuevo método.

2 - Duplicación en clases relacionadas : Es cuando se encuentra la misma expresión en dos o más subclases de una misma jerarquia de Clases. Es una duplicación bastante común también ya que provienen de comportamientos comunes de clases hermanas hijas de una misma Clase padre. La forma de solucionarlo es extraer el codigo duplicado en ambas subclases con "Extract Method" y luego si aplica a todas las subclases subirlo a la clase padre, si no aplica es una indicación de que hay un problema de diseño con la jerarquía de clases. A veces hay parte de una logica duplicada y otra parte no, en ese caso se puede convertir el método en un "Template Method" en la clase padre, abstracto y luego implementar la parte que es diferente en ambas subclases.

3 - Duplicación en clases no relacionadas : Como el titulo lo indica, es tener duplicación en dos métodos de dos clases A y B no relacionadas por herencia ni interfases. En este caso se debe considerar extraer el comportamiento duplicado en la clase A y generar una nueva clase C que contenga este codigo (refactoring "Extract Class") y luego reemplazar el código duplicado en la segunda clase B por una invocación a la nueva C. Otra posibilidad es que la lógica duplicada realmente pertenezca a una de las dos clases, y la otra debe invocar a la clase a la que pertenece el codigo. Una tercera posibilidad es que pertenezca a otra clase ya existente. Aplicando la Ley de Demeter, el Single Responsability Principle y el sentido común tenemos que encontrar el lugar adonde esta lógica pertenece y removerla de todo otro lugar.

4 - Duplicación en distintos lenguajes : Por ejemplo, validaciones que tenemos que hacer tanto en la GUI (por ejemplo en Javascript) y en el backend (por ejemplo en Fortran... :)). Este es un tipo de validación dificil de evitar. Una manera posible es escribir el código para uno de los lenguajes y generar el código del otro automáticamente a partir de este.

Para terminar les dejamos una regla práctica para aplicar cuando estamos ante una duplicación de código.

La regla del tres (Don Roberts)

La primera vez que uno escribe una lógica la escribe y listo. La segunda vez que uno hace algo similar debe fruncir el ceño, tomar nota y dejar la duplicacion de cualquier manera. La tercera vez que aparece la duplicación, bang!, uno debe eliminarla, a través de la refactorización.

Entonces, recuerden, Una de las Buenas Practicas de programación que más recomendamos es buscar todo Code Smell que encuentren en el codigo y removerlo a través de la refactorización.
Y si pensamos que el código duplicado es el origen de muchos males (Duplication is evil) y uno de los Code Smell más comunes, aprender a buscar, detectar y remover duplicaciones de código es una actividad a la que debemos aplicarnos si queremos convertirnos en Programadores (así con "P" mayúscula).

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.

miércoles, 25 de agosto de 2010

Code Smells : Obsesión Primitiva

Queridos Chimpances Entrenados, Dice la Enciclopedia Galatica:

Obsesión Primitiva: Es un Smell que se produce cuando uno utiliza tipos primitivos como Strings o Doubles o Ints para representar conceptos que realmente merecen ser representados por clases de objetos.

Es uno de los smells más comunes que podemos encontrar en cualquier código que escribamos o revisemos.

Cuando se trabaja con Objetos, para modelar la estructura interna de una clase uno tiene dos alternativas:


1 - Referenciar a otra Clase de objeto

2 - Utilizar un tipo primitivo.

En general el problema empieza cuando uno decide utilizar un tipo primitivo para modelar un concepto porque se resiste a crear una clase para el mismo porque en ese momento solo sirve para encapsular un simple entero, una String o un par de estos, con nada o muy poco comportamiento extra.

Tipicos ejemplos de esto es cuando uno modela por ejemplo un rango de enteros, como ser, rango de valores válidos para un atributo para un sistema que modele rangos de edades especificos, campos de calle, numero y ciudad para modelar la direccion de un Cliente, etc. . Otro ejemplo es utilizar un int o String para discriminar el "subtipo" o diferentes variaciones de un objeto. Pero el caso emblematico por definición es cuando uno modela sumas de Dinero como un Double para representar el valor, y una String que representa la especie (dolares, pesos, euros, etc.)

Cuando uno sigue trabajando luego de la elección del tipo primitivo al principio siente que ahorro en cantidad de código y clases creadas, y se felicita por ello.

Pronto, no obstante, a medida que el sistema crece comienza a pedir más y más comportamiento de ese tipo primitivo, que, como en general no puede agregarse en el mismo, en lenguajes como Java o C# , se terminan ensuciando el código de la clase adonde se encuentra el primitivo, adonde se agregan métodos y más métodos helpers o utils que trabajan con ese tipo primitivo y con ninguno de los otros atributos de la clase.

Sin entrar en campañas proselitistas, otros lenguajes si permiten agregar métodos no solo a las clases de los primitivos, sino también a instancias específicas (prototype) como por ejemplo en Ruby o Smalltalk, pero no obstante las reglas de Buen Diseño esta indicando la necesidad de un objeto que represente mejor el concepto y que va a permitir agregarle comportamiento a medida que la solución la vaya necesitando (no olvidemos: nunca especular que se necesitará por adelantado).

Ejemplo 1
package ar.com.smells.examples; 
public class ClienteFibertel { 
private String nombre;  
private  String apellido; 
private MedioPago medioPago; 
private long idCliente; 
 
... 
 
private String nombreCalle; 
private String numero; 
private String piso; 
private String Depto; 
private String codigoPostal; 
private String ciudad; 
 
//Metodos propios de clase Cliente 
 
   .... 
 
//Metodos que piden a grito una clase Direccion 
 
    public boolean esCodigoPostalRural() { 
     bla bla bla 
    }
    
    public String direccionFormateadaParaEnvioPostal() {
            bla bla bla 
    } 
 
    public boolean esEdificio() {
           bla bla y mas bla. 
    }
 
} 
En este caso, se deberia refactorizar a una clase Direccion que forme parte del Cliente y que deberia llevarse los tres métodos que estamos indicando que no forman parte de la lógica propia de la clase adonde están.

Ejemplo 2

Un ejemplo muy especial dentro de la obsesion por primitivos, aunque no es el tipico ejemplo, es cuando se utiliza un int como subtipo y esto genera que se repita la lógica de distinción entre subtipos entre todos los métodos de la Clase. La solución es muy clara, la clase debe separarse en una clase base y varias subclases con la diferencia.

 
public class ConexionInternet
{ 
   ... 
 
private int tipoConexion;
 ... 
 
    public String metodoA() { 
         if (tipoConexion == ConexionInternet.CABLEMODEM) { //ya no  hay mas, adios Fibertel
 
         } else if (tipoConexion == ConexionInternet.ADSL) { // speedy? arnet? }
 
           } else if (tipoConexion == ConexionInternet.Modem) { //El unico que va a quedar, la vuelta al 56K! 
 
             }
   } 
 
   public boolean metodoB() {
       if  (tipoConexion == ConexionInternet.CABLEMODEM) { //ya no hay mas, adios Fibertel
     
       } else if (tipoConexion == ConexionInternet.ADSL) { // speedy? arnet? 
 
       
        } else if (tipoConexion == ConexionInternet.Modem) { //El unico que va a quedar, la  vuelta al 56K! 
 
        } 
   }
 
} 

Es bastante lógico decir que parece razonable refactorizar en 3 clases ConexionInternetCableModem, ConexionInternetADSL, ConexionInternetModem, todas dependiendo de una clase abstracta o implementando una interfaz ConexionInternet.

De cualquier manera una de los ejemplos más clásicos y que mayores problemas trae al estar “Obsesionado por Primitivos” es cuando se modelan Sumas de dinero como objetos Double. Los que escribimos este blog damos plena fe de ello.

El primer problema aparece cuando uno comienza a realizar operaciones y surgen las "pequeñas" diferencias por redondeo!. Y claro, cuando uno tiene problemas de redondeo con kilometros entre planetas no hay mucho problema (salvo que este manejando una nave espacial :), pero si es dinero... ahi sii, esos centavos importan y como!

Luego la siguiente estación al desastre es cuando el Usuario, a pesar de habernos dicho que siempre trabajaba con dinero local (pesos, pesos Chilenos, reales, lo que sea) viene muy contentos a contarnos que ahora va a empezar a aceptar pagos en dolares, euros, y en… rupias indias!.

Y nosotros con la clase Producto definida asi:

 
public class Producto {
... 
private double precio; 
 
.... } 

Inmagínense el cambio en todo el sistema!

La solución es ampliamente conocida y no nos vamos a extender en ella por temor a hacer un post kilométrico, pero para los que les interesa pueden verla en detalle en el libro de Test Driven Development (Lo se, lo hemos recomendado muchas veces, de Kent Beck).

Para los ansiosos o remolones, la solucion es crear una clase Money que siga el patrón de diseño Value Object, sin mostrar el detalle de los métodos, sería algo asi:

 
public class Money { 
 
private BigDecimal monto; 
private Currency moneda; 
….
} 

Un hecho relativamente moderno que ha reafirmado e incentivado la Obsesión Primitiva es la utilización másiva de ORMs (Object Relational Mapping), como Hibernate.

El problema en este caso es cuando se esta definiendo un Bean que terminará como tabla en una base de datos y, y condicionados por la complejidad de la herramienta, se prefiere tener un bean al cual le agregan los atributos primitivos directamente y no múltiples tablas de objetos relacionados.

Por ejemplo, prefieren tener una sola clase SujetoAnses al cual le agreguen 2 int para guardar rango máximo y mínimo configurado a tener otra tabla relacionada con el Bean principal con los rangos permitidos.

O del ejemplo anterior, ClienteFibertel con todas las String en una misma tabla a tener una relacion con otra tabla Direccion. Es inútil explicar en estos casos que en general no es razonable decidir el Diseño de Objetos por las consecuencias que tendrán sobre la base de datos. Al menos no a priori hasta tener razones de peso, de performance, etc..

Para terminar, no estamos afirmando que TODO atributo o concepto simple debe ser representado como una clase de objetos que se convierta en simples wrappers de un int, String o double.

Pero ante el minimo comportamiento que haya que agregar, cuando uno comienza a pasar ese valor de aquí para allá o hace algún método que lo utiliza solo a él, etc., en esos casos mantenerlo como primitivo es… una obsesión y hay que tratarla!.

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!"

jueves, 22 de julio de 2010

¿Cuántos libros técnicos leiste este año?

"Tenes que leer este libro y presentarlo"

El primer libro técnico que leí por motivos laborales fue "Code Complete", de Steve McConnell, en el año 1996. En el trabajo en que estaba en ese entonces me dieron la asignación de leer el libro y presentar partes de el a mis compañeros. Empecé la tarea con pocas ganas, pero pronto me di cuenta de que la lectura de este libro iba a cambiar mi vida profesional para siempre. La posibilidad de "pararme en los hombros de un gigante" al aprender de alguien con tanta experiencia como McConnell me fascinó y a partir de ese momento he intentado siempre leer la mayor cantidad de libros técnicos posible.

Libros y capacitación

Un libro es probablemente la manera más económica de capacitarse. "The Pragmatic Programmer" de Andy Hunt y Dave Thomas (un libro excelente, si van a leer un sólo libro este año, lean este) cuesta 40 dolares en Amazon. Tomando en cuenta el envío probablemente el costo total debe rondar los 200-250 pesos. ¿Qué curso se puede tomar por ese precio, aun suponiendo que fuera posible encontrar uno que valga la pena?. Si se comparan las versiones electrónicas de los libros los costos son aún menores, ya que cuestan menos y además no hay que pagar envío.

Por lo tanto para una empresa que le de importancia a la capacitación, es posible lograr muy buenos resultados invirtiendo unos $1000 por mes. Por supuesto hay que tomar en cuanta que con sólo comprar los libros no alcanza y que hay que buscar maneras de lograr que los libros sean realmente leídos y aplicados. Una práctica que da muy buenos resultados es que los miembros de un equipo se compromentan a leer un libro y armar almuerzos quincenales o mensuales donde todos puedan discutir algunos capítulos o armar presentaciones y resúmenes.

¿Sigue valiendo la pena invertir en libros con toda la informacion disponible en la Web?

Depende del tipo de libro. Los libros de referencia probablemente no sean una buena inversión: es mucho más sencillo usar Google (y más barato, una vez compramos un libro de referencia sobre Java, llamado "Java Bible", fue muy útil....como soporte de la pata de una mesa). Sin embargo los libros que tratan sobre un tema desde una perspectiva más completa, si lo son. Por poner un ejemplo, no tiene sentido buscar como hacer paginación en Rails en otro lado que no sea Google (o Bing!, ;) pero si uno quiere entender realmente como funciona Rails solo lo va a poder hacer desde un libro, donde los conceptos van a estar presentados de una manera mucho más integral (y al respecto recomiendo el excelente "The Rails Way"). Esto tiene que ver con lo que contábamos en "cargo cult programming": para buscar una solución rápida a un problema probablemente el mejor recurso es la Web pero para entender realmente por qué funciona esa solución lo mejor es un libro (o un curso, claro que tomar un curso en EEUU o Europa no es para cualquiera).

Spik in inglish?

Una dificultad adicional que tenemos en países donde no se habla inglés es el idioma. El porcentaje de buenos libros técnicos traducidos al castellano es muy bajo y la calidad de las traducciones es muy mala (nunca voy a olvidar la traducción de buffers como "tampones de memoria" en un libro de redes de la época de la facultad). Esto hace que la incapacidad de leer correctamente inglés sea un escollo insalvable para ser un buen profesional. Si alguien que no sepa este idioma quiere dedicarse al desarrollo de software su mejor inversión (mejor que aprender cualquier tecnología) va a ser dedicarse a aprenderlo.

cuanto$?

Los programadores somos trabajadores intelectuales y por lo tanto nuestros conocimientos constituyen nuestro capital. Si dejamos de actualizarnos nos descapitalizamos y perdemos la posibilidad de conseguir mejores trabajos y ganar más dinero. Dedicar una pequeña parte de nuestro tiempo a leer libros es una manera de aumentar nuestro valor en el mercado y mejorar a la vez profesionalmente.

Entonces, ¿Cuántos libros técnicos leiste este año?

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.