Demo vs Review

Dos miradas sobre cómo presentarle a los clientes en qué se estuvo trabajando.

Random Google Image

Antes de comenzar quiero aclarar que en este artículo no estoy hablando puntualmente de una “Review meeting” tal cual plantean frameworks como SCRUM. Simplemente son dos formas de llevar a cabo una demostración del trabajo realizado a un tercero.

Es razonable que cada tanto nuestros clientes quieran ver el progreso, así como también lo es, que nosotros, orgullosos desarrolladores, queramos mostrar en lo que hemos estado trabajando.

No importa la metodología utilizada: si es ágil o no; si es por iteraciones cortas o largas; en algún momento debemos interactuar con el cliente para que éste pueda ver “lo nuevo”.

El ejemplo que voy a usar, si mal no recuerdo, lo usó Freddy Alegre en el AgileOpenCamp de este año en uno de sus charlas de openspace, así que el crédito es para él :).

Para ilustrar el ejemplo, supongamos que el cliente pidió que se le construya una silla. Vamos a mostrarle al cliente cómo avanza su producto con una demostración, utilizando dos miradas diferentes : la “demo” y la “review”.

Demo

Empecemos con una demo. Esta reunión suele ser un ambiente controlado, en donde una persona del equipo le muestra a los demás qué se hizo. Empieza contando como pusieron un cosa por acá, otra por allá y cómo es el flujo de trabajo.

El problema de las demo es que en general uno evita hacer cosas que potencialmente puedan generar un fallo durante la demostración (como ingresar letras en un campo solo numérico), por miedo a quedar en evidencia.

Random Google Image

Haciendo el paralelismo con la silla, uno al mostrarle la silla se la muestra de lejos, se sienta despacito y quizás solo en una parte procurando no cargar la pata que sabemos que está mal encolada para que no se rompa. Le contamos lo suave que es la madera y lo bien que siente al sentarse.

Si alguna vez fueron a comprar un sillón sabrán que se siente cuando te dicen “son de muestra, no te podes sentar”. E imaginen que siente el cliente cuando ve su producto andando en una pantalla.

Este tipo de reunión nos suele dejar poco feedback útil al equipo de desarrollo. Yo personalmente las veo más para cubrirse y decir “¿ves que avanzamos un montón?” que para generar un valor real al producto y por ende a los usuarios.

El mayor feedback que podemos obtener de una demo es visual : “¿podemos cambiar el color de ese botón?”, “¿podemos alinear eso a la derecha?”, etc.

Review

La review va un paso más. Los que se reúnen son los mismos que estaban para la demo, pero en lugar de mostrar una pantalla, lo que se hace es darle al cliente la opción de interactuar con el software, y a medida que le vamos contando los avances, que él vaya teniendo la posibilidad de descubrir cómo se hacen las cosas.

La ventaja de permitir al cliente utilizar el software es que puede darnos
un feedback mucho mas rico respecto a cómo se organizaron, las posiciones
de los elementos, los textos, etc. Como usuario uno puede detectar si es cómodo trabajar con el sistema o no.

Podemos ir un paso más y no explicarle siquiera las cosas, solamente decirle cosas como “ahora podes crear un usuario nuevo, y luego darle permisos de administrador” que que él deba hacerlo con la menor asistencia posible. Esto nos permite probar nuestras si la experiencia de usuario es como la pensamos o si le dificulta encontrar donde se realiza cada tarea.

En el caso de la silla podría descubrir que su peso es demasiado y pedir que se refuercen las patas; o que la madera elegida se ve linda y suave pero haría falta un almohadón ya que después de 10 minutos sus posaderas se sienten incómodas usando el producto :).

Random Google Image

Existen muchas posibles ramificaciones para un review y generalmente las empresas de servicios les escapan porque el control está más del lado del cliente. Pero los beneficios para el cliente son infinitamente mejores.

También hay que tener en cuenta que aunque es el cliente quién usa el software, está todo el equipo de desarrollo durante la reunión para guiar al usuario que hace la prueba del sistema. La reunión debe ser igualmente planificada y estar alineada con los objetivos de la iteración. No es una reunión aleatoria que va para donde quiere.

Cerrando

Como se dijo antes, una review permite al usuario final o cliente tener un contacto real con el software, pudiendo detectar mejoras con mayor rapidez que el caso en donde es observador pasivo.

Si bien hay mayor riesgo de que algo salga mal, ese riesgo puede ser mitigado de muchas otras formas durante el desarrollo y las ganancias son aún mayores que el riesgo de aceptar que algo no salió bien.

Sea cual sea el método que elijan, algunos pequeños consejos:

  • Todos para uno: Que participe todo el equipo tanto en la preparación como en la reunión. Escuchar el feedback de primera mano es mucho más eficiente que jugar al teléfono descompuesto.
  • Planificar: Elegir en que orden van a presentar las cosas y armar un guión para seguir un orden.
  • Preparar: Crear de antemano todo lo que sepamos que necesitamos en el ambiente donde vamos a probar. Usuarios, imágenes, reportes, datos de prueba, etc.
  • Practicar: Al menos una vez para ver que van a decir en cada caso de no quedar balbuceando mientras piensan “¿Cómo era esto?”.
  • Documentar: Todo lo que se dice en la reunión que de valor al producto.

Los invito en su próxima entrega a hacer una review en lugar de una demo y contarme cómo les fue :).

Lo que quiero vs Lo que obtengo

¿Cuántas veces recibiste algo y te diste cuenta que no es lo que pediste?, o tal vez debería decir ¿… y te diste cuenta que no es lo que esperabas?

Random Google Image

Cuando uno avanza en su carrera es normal que le den la responsabilidad de coordinar o revisar el trabajo de otros. Este trabajo por hacer son los requerimientos del sistema en el que estamos trabajando y dependiendo de cómo sea el entorno de cada equipo, estos requerimientos pueden estar mejor o peor definidos, o más bien, mejor o peor documentados.

Últimamente me suele pasar que lo que yo esperaba ver en el momento de la revisión no era lo que recibía. Lo primero que empecé a pensar es que estaba siendo muy exigente y que debía bajar mis expectativas.

Lo importante no es lo que dices sino lo que la gente entiende. Words That Work, Dr. Frank Luntz.

La respuesta “fácil” es pensar que el programador hizo lo que quiso; que no entendió lo que le pedí; o que tiene poca experiencia y por eso interpreta mal las cosas (hay ejemplos reales de todas esas afirmaciones pero no vienen al caso :D). Pero hay un factor clave que no estaba teniendo en cuenta: como comunico lo que quiero a los demás. Haciendo una retrospectiva me di cuenta de que prácticamente les estaba tirando tickets por la cabeza sin mayor contexto.

A partir de estas observaciones es donde comencé a preguntarme qué es lo que estaba haciendo mal y la respuesta a la que llegué se puede resumir como la “maldición del conocimiento”.

La maldición del conocimiento: Una vez que conocemos algo, nos resulta muy duro imaginarnos cómo era no conocerlo.

En el momento en que cae un problema nuevo y uno comienza a definir cómo lo resolvería, ya comienza a tener un pre-concepto de qué es lo que espera uno ver. Si cuando le pedimos a otra persona que resuelva el problema por nosotros, no le transferimos ese conocimiento, es cuando se genera este “roce” que nos hace doler la panza y pensar “¡¿por qué lo implementó de esta manera?!”; o darse cuenta de que hay ciertos casos que no estaban contemplados por la persona que resolvió el problema. Quizás ni siquiera estaban claros que esos faltantes eran un problema.

El efecto que observo de este tipo de problemas es que el proyecto “está atrasado”. “¿No entregábamos ayer?” podría preguntar alguien. Quizás de haber estado bien comunicado de entrada ser podía prever que “ayer” no era posible entregar. Quizás de haber comunicado de manera diferente, la persona que realizó el trabajo podría haber sido más efectiva.

Soluciones a este problema hay varias, según cada equipo y organización puede que una u otra se adapte mejor.

La primer opción y quizás la más tradicional es contar con un Analista Funcional que se encargue de especificar al detalle cada problema de cada proyecto de cada cliente. Si bien no estoy en contra de esto, creo que no escala para el día a día. En nuestra industria los requerimientos cambian muy rápido y dejan obsoleta muchas veces cualquier planificación. Sí nos suele servir, a nosotros como empresa, para el kickoff, donde se puede recolectar una primera aproximación de lo que quiere el cliente.

Una segunda opción es que alguno de los empleados más experimentados haga de “Analista” y se pase horas escribiendo lo que pretende que hagan los demás desarrolladores. Es lo que en general hago ahora, salvo en algunos casos, pero no me funciona por un par de razones.

La primera razón es porque la cantidad de tickets pueden ser decenas o cientos. Y especificar cosas que ni el cliente ni yo sabemos cómo las haríamos (o si siquiera se quieren hacer) carece de sentido práctico y termina siendo una pérdida de tiempo. Además nada garantiza que pueda ver todos los ángulos del problema para dejar claro que quiero.

La segunda razón y tal vez la más importante, es que para que esto sea útil lo tendría que hacer bajo demanda, es decir, cada vez que alguien va a empezar un ticket, agarrarlo, pensarlo, documentarlo y delegarlo. ¿Se imaginan cuánto tiempo les quedaría además para hacer algo realmente productivo?. Cero!. Eso sin contar que pasaríamos a ser el cuello de botella de todos los proyectos.


En el último post hablé de buscar mejores formas de construir software, y si algo me han enseñado mis años participando en comunidades de software libre es que compartir el conocimiento y la responsabilidad es beneficioso para todos.

Los frameworks ágiles, como Scrum, plantean este cambio de conducta. Es por eso que existe la Planning meeting o el refinamiento del backlog, momentos en el que el equipo se reúne completo, junto con el dueño del producto (puede o no ser el cliente real) a entender las tareas pendientes, hacer preguntas y documentar que se va a hacer en cada caso.

Está claro que la premisa fundamental para “estar todos en la misma página” es eliminar los intermediarios y que los temas se discutan abiertamente.

Evitemos le teléfono descompuesto

Como ganancia extra, involucrar a las personas les da un sentido de pertenencia que genera una responsabilidad en ellas, y por lo tanto un mayor compromiso hacia su trabajo siendo un beneficio tanto para ellos como para el cliente. Pueden leer más al respecto en Equipos Estables por sobre Pool de Recursos escrito por Martin Alaimo.

Para ir cerrando, les dejo una lista de tareas con cosas que creo que deberían ayudar y que tengo intención de ir mejorando :

  • Involucrar a todos, siempre.
  • Hacerlos participar, es decir, dejarlos hablar y escucharlos.
  • Incentivar el debate, tener dos ideas es mejor que tener una :).
  • Analizar pro y contras de las opciones y dejar documentado lo que se decida.

Para terminar, me gustaría cerrar con una pregunta para seguir aprendiendo: ¿Qué hacés en tu equipo/empresa para mejorar la forma en que se comunican?

Ser ágil es …

Si hay algo que aprendí en el último año es que la frase con la que titulé esta entrada es una de las cosas más difíciles de explicar que me encontré en el último tiempo.

Estas semanas estuvimos hablando mucho en el trabajo sobre agilidad, equipos, ser ágiles, Scrum, etc; y algo que he notado es que cada uno interpreta diferentes cosas sobre el qué y el cómo. Y en general me es muy difícil explicar la “particular” mirada que tengo actualmente de lo que entiendo por “ser ágil”.

Sacada de http://pragmaticprojectleadership.com/agile-mindset-deciding-work/

Hasta hace poco mi definición más corta para responder a esta pregunta era “ser ágil es responder rápido al cambio”. Después de largas horas de charlas durante el Agile Open Camp de este año, me di cuenta que mi respuesta no me dejaba satisfecho.

¿Somos ágiles porque hacemos iteraciones?, ¿somos ágiles porque hacemos entregas continuamente?, ¿somos más rápidos por ser ágiles?, ¿somos ágiles porque usamos Taiga, Jira, Trello?. Hay ejemplos y contra ejemplos a montones que responden estos interrogantes en varios blogs.

Para mi “ser ágil” representó un cambio mental, un enfoque diferente a lo que estaba acostumbrado a hacer. Y ese cambio mental es simplemente la búsqueda de la agilidad, no el ser ágil en sí. Ágil es un ideal. Un lugar donde queremos llegar. El camino para ir a ese lugar es lo que creo que nos convierte en agilistas. Y en ese camino no hay solo procesos y herramientas, hay personas. La forma en que interactuamos con las personas es la que nos acerca al objetivo.

El ser ágil plantea una perspectiva, una dirección hacia donde queremos ir. El Manifiesto ágil lo plantea muy bien en sus primera palabras

We are uncovering better ways of developing software …

Ser ágil se trata simplemente de eso. Buscar mejores formas. ¿Scrum te funciona? Genial usalo! ¿Kanban es más lo tuyo porque no podes planificar por iteraciones? Excelente, usalo! Experimentá, buscá tu espacio y tratá de ir evaluando y mejorando constantemente en busca de ese ideal ágil.

En uno de los proyectos donde estoy involucrado estamos atravesando justamente esta transformación. No usamos Scrum; no usamos Kanban; no hacemos XP; ni RUP :D. Pero sí implementamos varios cambios que buscan mejorar la forma en la que creamos el producto para el cliente.


Hablamos entre todos

El primer cambio que acordamos fue dejar de lado los chats uno a uno y nos ‘forzamos’ a hablar en el canal de Slack del proyecto.

Lo que nos venía pasando es que caía un ticket a resolver, lo tomaba alguien del equipo, consultaba con otra persona del equipo, llegaba un pedido para que se revise el código y acto seguido quien revisaba preguntaba : “¿pero no podemos hacerlo de esta otra forma que es más simple?”.

En general uno encuentra que siempre había una alternativa menos costosa que uno no pensó. Nos estábamos perdiendo la parte de la interacción de las personas :).

Esto está ayudando mucho porque todos, aunque no estemos 100% del tiempo en el proyecto, podemos estar al tanto de lo que está pasando, opinar cuando ve que otro tiene un problema, o simplemente ofrecer otra perspectiva.

Un lado “negativo” que se genera es que gente que está de paso por el canal vea que hay “mucho ruido”. Si esto pasa recomiendo tener varios canales para el proyecto: uno de cosas casual / novedades y otro más movido donde se hable de todo, los bots peguen los resultados de las tareas automáticas, etc. También podemos sugerirle a esta gente la “regla de los dos pies” virtual :).

https://www.flickr.com/photos/planeta/3311035191

Y como cierre de este punto: hablen entre Uds a ver qué es lo mejor para este equipo en este proyecto. Júntense una o dos horas en una sala de reunión, plaza, bar, o donde les pinte y traten de encontrar el mejor canal para intercambiar información de manera efectiva.


Eliminamos distracciones.

El Manager, antes de los cambios, solía interrumpir por cada consulta del cliente a los desarrolladores para atacar diferentes tareas: dejar lo que se estaba haciendo para empezar una tarea nueva (dejando la actual por la mitad); investigar un bug para ver si es un problema grave; o simplemente para hacer de proxy con alguna pregunta del cliente.

Esto hacía que el cambio de contexto sea constante y la productividad y el ánimo decaía.

Para atacar este punto lo que hicimos fue co-crear una matriz de Eisenhower que nos permite priorizar las cosas en urgente / importantes. Para tener un criterio común y evitar ambigüedad definimos que lo importante es lo que el cliente quiere. Mientras que lo urgente es lo que afecta al usuario del sitio.

Con esta categorización pudimos acordar que solo lo urgente e importante puede ser tratado fuera de las reuniones de sincronización. Éstas reuniones las hacemos dos veces por semana : lunes y miércoles sobre las 14 hs (el horario es flexible).

Entonces, si llega un pedido del cliente, el Manager puede decidir si es necesario interrumpir el trabajo normal o si puede esperar a la próxima reunión, que en el peor de los casos es dentro de 48 hs.

Recién estamos viendo los primeros resultados y fueron en semanas bastante relajadas, pero me animo a decir que el equipo está más feliz, el cliente obtiene más cosas “terminadas” por semana y seguramente empecemos a bajar la tasa de tickets reabiertos por hacerlos apurados o desconcentrados.


A simple vista pueden parecer cambios triviales, pero el proceso de llevarlos a cabo no lo es. El trabajo que hace el equipo día a día para mejorar estos mismos puntos es increíble.

Me animo a decir que este es uno de los proyecto más ágiles en los que he trabajado últimamente :). Ah, y todo usando solo Redmine y Slack :D, las mismas herramientas que usábamos antes.

Encontrar el punto de equilibro no es fácil, sobre todo en una software factory. Quizás cuando trabajás en un producto se da todo más natural, pero no es imposible. Mi consejo acá es ir con cambios pequeños que no sean muy disruptivos a lo que ya está instaurado como cultura en la empresa y luego a medida que se vayan dando los resultados la gente se va a ir contagiando sola.

Para cerrar quiero dejarles algunas recomendaciones que he recibido y que busco aplicar en mayor o menor medida y que creo que pueden ayudar a cualquiera a empezar en el camino del agilista :

  • Comunica tus intenciones, con todos y a todos.
  • Trata de evitar las reuniones donde no esté todo el equipo, si hay algo anti-ágil para mi, es tener sub-equipos (si jugaron de chicos al teléfono descompuesto, sabrán de qué hablo :).
  • Mantené tu equipo en un tamaño razonable. Tampoco es útil reuniones de 200 personas :).
  • Introducí cambios que mejoren el equipo, el proyecto va a mejorar por el solo hecho de que el equipo esté más contento.
  • No te centres en las herramientas, centrate en las personas.

¡No experimentes!

http://www.picserver.org/images/highway/phrases/experiment.jpg

En las empresas de sistemas nos caracterizamos por ser mayormente gente analítica, matemática (por no decir “cuadrada”) y que tenemos muy presente el método científico. Sin embargo cuando vamos creciendo y teniendo más responsabilidades nos vamos encontrando un cierto rechazo o miedo a la palabra “experimento”.

Hace unas semanas luego de volver del Agile Open Camp 2017 decidí que era hora de hacer algunas cambios en un proyecto en el cual colaboro más desde lo organizacional que desde lo técnico. Volví con un montón de ideas para mejorar la forma de trabajar del equipo, pagar la deuda técnica acumulada, ser más robustos a futuro y muchas otras cosas.

¡Es hora de experimentar con este proyecto!

http://www.crecereningles.com/wp-content/uploads/You-Shall-Not-Pass-720×340.png

¿Experimentar?, ¿con un proyecto en producción?, ¿estás loco?, ¿y si el cliente se entera?, ¿y si rompemos algo?, ¿y si no sale bien? … ¿y si …?

Alguna de esas frases te pueden sonar familiar y son producto principalmente, a mi entender, de un problema de comunicación, o más precisamente de cómo comunicamos nuestras intenciones.

Siempre que alguien levanta una ceja frente al planteo de un experimento, siento que hay un tabú frente a esta palabra; una especie de pensamiento negativo en el cual el otro se imagina que queremos romper algo a propósito y que las consecuencias van a ser terribles e irreparables.

La mala noticia es que los experimentos fallan, pero no son fallas terribles y mucho menos irreparables :). Podemos simplemente dejar de hacer lo que no funciona y volver a como lo hacíamos antes.

En mi experiencia, la gente reacciona mucho mejor si uno plantea la pregunta de otra forma : “¿te parece si probamos …?”, “¿y si cambiamos …?”, debe ser puramente psicológico, pero me dan más libertad de acción.

¿Por qué entonces seguir insistiendo con “experimentos”?, ¿No es entonces un problema semántico? se preguntará el lector.

Para mi un experimento genera un compromiso mayor. Cuando experimentamos no estamos simplemente planteando cambiar algo de manera arbitraria sino más bien estamos buscando solucionar un problema específico que ya tenemos identificado.

Y por si fuera mejor, para saber si el experimento sirvió de algo, debemos poder medir el éxito, por lo cual podemos generar métricas a partir del experimento. ¿Que manager puede resistir tener más métricas ;)?

Cambiar, por otro lado, es un mero acto aislado que puede o no medirse; puede o no generar conocimiento; y lo más probable que quede en el olvido.

Un experimento plantea un cambio, pero un cambio no genera un experimento por si solo.

Si todavía no estas convencido, te invito a experimentar en tus proyectos. Si no estás seguro de por donde empezar podés usar la guía ágil de experimentos de Kleer :



Cuando terminen los experimentos que estoy corriendo actualmente es probable que vuelva sobre este tema para contar qué hicimos, cómo lo medimos y que tal nos resultó.

No dejes que tu código decida

O al menos no lo dejes decidir todo el tiempo 🙂

Una de las cosas más difícil de la Programación Orientada a Objetos es mantener el foco en generar un buen código y no perderse en la tentación de generar un mar de condicionales (a.k.a. if’s). Es este artículo la idea es mostrar al lector que es posible generar código que no contenga condicionales, o sea al menos que sean pocos.

El problema con tener condicionales es que nos genera caminos alternos; cada camino puede tener su propia consecuencia; por lo que cada vez que queremos probar, debemos hacerlo por todos los caminos posibles. Si tenemos condicionales anidados la cantidad de caminos crece aún más, por lo que desde un punto de vista ideal, un código que tenga un solo camino posible es más fácil de probar (manual o automáticamente).

Hoy estábamos agregando sonidos a Jumping Code para Android, juego que estamos desarrollando en PatagonianTech y en uno de los pulls viene el siguiente código :

https://gist.github.com/Gazer/d106ad38b4cd858f279f9aac5a0f586d

Cada vez que saltamos, reproducimos el sonido correspondiente. Pero como somos bueno, dejamos que el usuario pueda prender y apagar el sonido a gusto, porque hay momentos en que uno quiere jugar en silencio :).

El problema con este tipo de aproximaciones suele ser :

  • Por cada evento que produce sonido, tenemos que agregar 4 líneas de código
  • Al utilizar una “Utility class” con métodos estáticos hacemos más difícil la posibilidad de agregar pruebas unitarias.

La solución más elegante, que a mi me gusta aplicar, en estos casos es utilizar un Factory primero para obtener un ‘SoundManager’ y luego tener 2 implementaciones : ‘LoudSoundManager’ y ‘SilentSoundManager’.

En este caso, como estamos con Java en Android, vamos a usar una interfaz para definir al manager genérico :

https://gist.github.com/Gazer/f128117877b82598b52b04d4f411bfdd

Ahora, si queremos reescribir el fragmento original simplemente tenemos que utilizar un SoundManager que nos será dado de alguna manera a determinar :

https://gist.github.com/Gazer/372616b12db49bc3263cc35349e8f060

Si tenemos 10 sonidos, vamos a cambiar 40 líneas de código que contienen if’s, llaves y condiciones por 10 simples llamadas a métodos. Es difícil negar lo útil de esta abstracción :).

Para implementar el loud manager (el que hace ruidos, digamos) podemos hacerlo bien simple reutilizando lo que ya teníamos inicialmente :

https://gist.github.com/Gazer/d405575ef50fee624b0730c830dc8b87

Para cuando no tenemos sonido, podríamos decir que SoundManager es null lo cual es tan malo como el caso inicial, porque tenemos que hacer el if de todos modos y peor aún porque si nos olvidemos estamos en un potencial NPE . Es por eso que lo mejor es aplicar el “Null Object Pattern”, que básicamente consiste en tener una implementación de la interfaz que no haga nada :

https://gist.github.com/Gazer/5c221c5ed446c026b69647836b2dbec0

Por último lo que necesitamos es una manera de obtener el manager correcto según las preferencias del usuario. Podemos hacer algo muy complicado o tener simplemente un “Factory Method” en algún lugar :

https://gist.github.com/Gazer/1ae044ff0a6ec9e2bbea9285b2a8e2d1

Este ejemplo es muy simple, pero multiplicado por cada decisión que hacemos o que dejamos hacer al código, termina en códigos que son difíciles de probar, tanto manual como automáticamente y poco agradables a la vista.

Los patrones son herramientas que como desarrolladores debemos tener presente para aplicarlas según sea necesario, a fin simplificar el código escrito para que sea más claro de entender y más simple de probar.


Existen otras alternativas que no requieren tanta ceremonia de crear una interfaz y dos implementaciones. La solución presentada no es la única ni la mejor. Es responsabilidad de Uds evaluar en cada caso el beneficio y el costo de implementar una posible solución a un problema determinado.

Métricas para mejorar nuestro código

Unas de las tareas divertidas que me toca hacer durante el día es el Code Review de varios proyectos. Es una práctica que utilizamos a diario en Patagonian Tech porque nos permite ayudar a los más nuevos a crecer como programadores y a su vez a mantener los productos que desarrollamos para nuestros clientes con un nivel bajo de errores.

Humor: posible métrica para evaluar el buen código.

El otro día estaba haciendo un review a uno de mis compañeros, el código no tenía falencias en su lógica, tampoco en su sintaxis. Luego de mirarlo una y otra vez me puse a pensar que “sonaba complicado”.

El fragmento de código en cuestión es el siguiente :

https://gist.github.com/Gazer/e12f6935bbdf405a97d5b045ff510544

En la primera pasada no acote nada, pero luego de estar dando vueltas un rato por mi cabeza, algo comenzó a hacer ruido.

Usualmente me gustan los códigos cortos, de pocas líneas, porque cada línea de código que agregamos es un potencial punto para introducir un bug. Pero no un bug hoy, uno a futuro, cuando otro programador pase y quiera tocar algo.

En el ejemplo de arriba, ¿qué pasa si hay otro Exercise que tiene una unidad diferente de “minutos”? ¿Debo agregar otro else if?. ¿Qué pasa si esa misma lógica está en otro lado del sistema? ¿Tengo que buscar por todo el proyecto?; ¿cómo sabe el programador que debe hacer eso?; ¿qué pasa si escribo las constantes “(floor_count)” mal?.

Como podemos ver, son muchos “qué pasa si”. Y cada uno es un potencial bug en la vida del proyecto.

Pero antes de refactorizar, también deberíamos preguntarnos : ¿vale la pena refactorizar?, usualmente un refactor implica un cambio; un cambio implica que podemos romper algo; lo que implica que debemos probar todo a fondo; lo que se traduce en tiempo y el tiempo es dinero.


Afortunadamente podemos sacar varias métricas de nuestro código que nos permiten elegir qué código necesita más urgente un refactor que otro. Acá un resumen de las que me parecen que pueden ayudarnos.


Número de líneas

Yo personalmente vengo del mundo de Ruby, donde un método siempre es bueno que sea corto, si es de una línea mejor, ya que es más simple con solo mirarlo que problemas podemos tener.

La “regla del pulgar” para mi suele ser que lo más corto que cumpla su función. Pero si consideramos el código de arriba como la mínima expresión, nada debería cambiar, verdad?

La realidad es que para apoyar esta teoría de contar la cantidad de líneas debemos apoyarnos en otras teorías, como ‘Principio de responsabilidad única’ y la ‘Ley de Demeter’.

El primero dice que una clase debe ser responsable de hacer una sola cosa. La segunda habla de que debemos tener bajo acoplamiento entre nuestros componentes.

Complejidad Ciclomática

Esta métrica nos permite medir “que tan complejo” es nuestro programa, teniendo en cuenta las ramificaciones, bucles, etc. Obviamente cuanto menor es la complejidad, mejor nos va.

Explicándolo de forma simple, la complejidad se calcula a partir de crear un grafo con la estructura del código y luego se puede calcular con una simple fórmula en base a la cantidad de nodos y aristas. Tambien hay herramientas que permiten calcular automáticamente la complejidad.

En este caso concreto la complejidad es baja (da menor de 10) por lo que no sería la principal métrica para decidir que se merece un refactor.

Número de entidades

Esta métrica a mi me gusta para saber un poco el acople del método actual con el sistema en general. Básicamente lo que hago es contar cuántas clases intervienen en la solución del problema. En el caso de nuestro análisis tenemos 2 : Exercise y ExerciseEntry. ¿Es necesario tratar con las dos directamente?

Basta con saber que ExerciseEntry está compuesto por un Exercise para que la respuesta sea clara y apoyándonos de nuevo en la “Ley de Demeter” seguramente la respuesta será que no.


Refactorizando el problema

Si miramos bien el código, el programa anterior se trata de transformar una lista en otra cosa. En esta transformación lo primero que se necesita es saber la unidad de medida. ¿Es responsabilidad de este “tranformador” calcular la unidad de la entrada de un ejercicio?; ¿O cada ejercicio debería saber que unidad tiene?

La respuesta correcta es la segunda, ya que aplicamos el “Principio de responsabilidad única“. Lo más razonable es pasar la lógica para calcular la unidad a la clase Exercise. El primer refactor nos deja el código como sigue :

https://gist.github.com/Gazer/b4f126a5a193577af089ca04fe1035e5

El bloque que realiza la conversión se ve mucho más limpio ahora. Y es más fácil de revisar a simple vista si hay algún error. Además podemos probar con mayor claridad que la detección de la unidad de un ejercicio sea correcta y hasta podríamos agregar pruebas automatizados para prevenir errores futuros. También si analizamos la complejidad ciclomática de cada método por separado encontraríamos una disminución de esta métrica, siendo algo positivo.

La “Ley de Demeter” por su lado nos dice que debemos tener ‘bajo acople’ entre los componentes. ¿Qué significa esto específicamente?. Supongamos que por algún refactor definimos que cada ExerciseEntry va a tener su propia unidad independientemente de lo que diga el Exercise asociado. El refactor va a ser complejo ya que debemos buscar por todo el código las llamadas anidadas y seguramente en algún lado va a quedar un exerciseEntry.exercise.unit() que en algún momento quedará como un bug.

Para lograr bajo acople lo que debemos hacer es nunca saltear al intermediario, aun si eso hace que tengamos un método como “proxy”. En este caso la idea seria llamar simplemente a exerciseEntry.unit() quedando el código de la siguiente manera :

https://gist.github.com/Gazer/b73eaed7a0fd98091651e393feaf1faa

Con un poco de magia de Groovy podemos dejar las cosas un poquito más organizadas :

https://gist.github.com/Gazer/9377ae32c97b4f644e79c04febcd4909

Ahora es la parte donde van al principio del artículo y comparar cual de los dos es más claro, y cual creen que podría tener menos errores.

El fragmento de código original de 17 líneas fue reducido a 7. El objetivo de este fragmento es muy claro ahora y ganamos dos métodos auxiliares que seguramente serán útiles en el futuro, ya que esas clases se utilizan en otros lados del sistema.


Cerrando

Podemos concluir que no solo debemos escribir el código que cumpla con lo pedido, también tenemos que escribir código que, sin llegar a ser críptico e inentendible, sea conciso y robusto, que resiste el tiempo y que minimice la posibilidad de que un tercero introduzca un bug.

Es importante configurar herramientas como GMetric en los sistemas de integración continua para atacar estos problemas antes de que pasen al olvido. Adicionalmente también continuar instruyendo y debatiendo en los code reviews para seguir aumentando la calidad de nuestro software.

Minimizando dependencias en Android

Es común que cuando nos encontramos frente a un problema lo primero que atinamos es a buscar una biblioteca que ya provea una solución. Si bien es una buena práctica (Don’t repeat yourself) muchas veces esta solución trae más problemas a futuro que soluciones.

Un caso particular que se suele ver mucho en Android es la necesidad de hacer la experiencia del usuario más rica, de darle más información en pantalla. Para ellos agregamos toda clase de detalles a nuestras UIs, y como escribir vistas desde cero suele ser complejo, terminamos incluyendo una biblioteca como dependencia seleccionada de alguna de las opciones populares.

En general estoy de acuerdo con esa aproximación, pero siempre está el fantasma de los 65K de Android. ¿Entonces qué hacemos?. Simple, escribamos nuestras propias vistas donde sea fácil, trivial o simplemente tenga sentido.

Cabe mencionar que otra posible táctica a este problema es utilizar ProGuard, a fin de eliminar todos los métodos y clases que no se usen. Trataremos de analizar en un próximo artículo esta posibilidad.


Indicador de página

Para ilustrar el punto voy a utilizar un problema con el que me encontré esta semana : agregar a un ViewPager los ‘puntitos’ que indican de manera visual en qué página estoy y cuantas páginas tengo.

Indicardor de páginas de un ViewPager

Si quisiera agregar esto rápidamente al proyecto, con hacer una simple búsqueda en Google encontraría varias opciones. Tal vez una de las más populares es ViewPagerIndicator escrita por Jake Wharton.

Esta biblioteca nos ofrece gran variedad de configuraciones y opciones : círculos, líneas, imágenes personalizadas, con tabs, sin tabs, etc. Yo en principio no necesito tantas cosas. Solo quiero un círculo por página y que el círculo que representa la página actual sea de otro color.

Para hacer el problemas aún más interesante, en esta aplicación estoy llegando a los 62k métodos, lo que me deja poco para maniobrar en cuanto a dependencias se refiere.

Haciendo una cuenta rápida la biblioteca tiene 9 archivos Java, si suponemos que cada uno es una clase y cada uno puede tener alrededor de 30 métodos, estamos casi en 300 métodos solo para mostrar 3 círculos de color blanco.

Vamos a ver a continuación que en unos 12 podemos implementar lo mínimo e indispensable para este proyecto.

Nota : Contar los métodos no es tan lineal, las cuentas son solo a fin de ilustrar el punto. Si te interesa saber que pasa con Java en Android abajo del capot, podés ver la charla de Jake Wharton ‘Exploring Java’s Hidden Costs’.

PagerIndicator.java

Para implementar un indicador de página no necesitamos mucho :

  • Saber cuantas páginas tenemos
  • Saber cuál es la página actual
  • Saber el tamaño de la vista y de los círculos
  • Saber cuando el usuario cambia de página

Lo primero que debemos hacer es crear el esqueleto base de nuestra vista con los constructores que necesita. Cuando extendemos View nos pide que definamos un constructor. Si bien con solo definir el primero alcanzaría para este caso, siempre es bueno definirlos todos, sobre todo si queremos agregar luego la posibilidad de customizar la vista desde el layout XML.

https://gist.github.com/Gazer/a3e8f61031a013aab690c75aeccb0b60

Antes de empezar a dibujar algo vamos a necesitar algunos objetos básicos. Como regla general es malo crear instancias de cualquier cosa en el ciclo de dibujo. Recuerden que debemos poder mantener 60fps en caso de querer animar y si el garbage collector está en el medio eso no es alcanzable.

Este método setup() que creamos será llamado desde todos los constructores que definimos anteriormente, de manera que no importe como se cree la vista tendremos todo lo necesario. Los objetos Paint nos permiten definir cómo se van a dibujar las cosas.

https://gist.github.com/Gazer/5098ec213709d3bac7263d657cca6188

Dibujar los círculos que representan las páginas es bastante simple en este caso. Lo que hago es calcular el ancho total que van a ocupar los N puntos, contando los espacios de separación que son proporcionales al diámetro y luego centro el bloque completo tanto en dirección horizontal como vertical.

Un punto crucial en toda vista es definir el tamaño. En Android un programador puede decir que quiere que una viste ocupe todo el padre, que se adapte al tamaño del contenido, que tenga un tamaño exacto. Esto tanto vertical como horizontal. Una forma de resolver este problema es definir el tamaño mínimo en cada eje y luego ver que es lo que quiere el programador. Una vez que tenemos ambos, nos quedamos con la mejor opción.

Todos estos cálculos se hacen en la pasada de layout, específicamente la llamada a onMeasure es la que “mide” la vista y le avisa al padre el tamaño que vamos a tener.

Siempre que calculemos el tamaño de nuestra vista es fundamental tener en cuenta el padding que el programador está deseando para nuestra vista. En este caso nuestra vista, por ejemplo, queremos que sea como mínimo el diámetro elegido más el padding.

https://gist.github.com/Gazer/6ecbed1b078ac0c2a07608e533698c13

Ya con todas las piezas en su lugar podemos proceder a escribir el método onDraw de nuestra vista, que es donde finalmente se pone interesante.

Todas las vistas en Android son utilizables no solo en nuestras aplicaciones, sino que también podemos verlas en el editor visual del Android Studio. Es por eso que el framework nos da la posibilidad de preguntar en que contexto se va a dibujar la vista. En este caso lo que se hizo fue dibujar 5 círculos con el del medio como página actual en el editor, de manera de poder tener una buena vista previa de la vista en la ventana.

https://gist.github.com/Gazer/dc1f39f6c5787a0d19240350855b5ca2

Como último paso y no por eso menos importante, nos falta conectar con el ViewPager y saber cuando éste último cambia de página. Para eso basta con agregar un PageChangeListener y el resto es historia 🙂

https://gist.github.com/Gazer/09c80557fda8b9d2b91d6953d5afbec4


Como se puede ver hay casos que son muy simples de resolver y evitar así una dependencia externa.

A esta implementación se le pueden mejorar un par de cosas :

  • Utilizar attributes para configurar el radio, color, forma
  • Utilizar el onPageScrolled para animar el cambio de página

Pero eso queda como desafío para el lector :). El código completo de la clase se los dejo en este Gist.