Developing Frogtek

El blog del Departamento de Tecnología

Etiqueta: eficiencia

¿Para qué sirve ser ágil en una start-up?

Mucho agile, mucho agile… pero si te descuidas puedes acabar en una empresa en la que lo único agile es el motor y para de contar, más cuando el equipo de producción, como en nuestro caso, está aislado del resto o directamente, como pasa en las consultoras, no hay resto. Ésta es un poco la conclusión que he obtenido tras leer los libros “Lean Start-up Method” de Eric Ries, “The four steps to the epiphany” de Steve Blank y después de conocer un poco el mundillo éste del agilismo en España… y por experiencia propia, por qué no decirlo.

Supongo que en parte es porque el mundo del software ibérico está principalmente orientado a programar para terceros y en ese caso ser ágil es “tan fácil” (y tan difícil) como pegarte unos años hasta que haces scrum, tdd y demás como Dios manda y conseguir que el cliente acuda a las reuniones y se involucre en el proceso de cada sprint. Pero la realidad es que detrás de los métodos ágiles hay mucho más y es algo de lo que no se oye hablar demasiado. Hace mucho que no acudo a ninguna AOS, ni CAS (siempre me coinciden con algo que no puedo dejar para otro momento), pero la primera CAS a la que acudí (2010) me encantó, sabíamos tan poco de métodos ágiles que cada charla era un nuevo mundo de conocimientos y posibilidades. Desde entonces si no yo, siempre alguien de Frogtek ha acudido a estos eventos y trasladado lo que allí ha aprendido. Últimamente, sin embargo, me empezó a sorprender que si bien toda la comunidad está muy interesada en ser técnicamente ágiles, no parece haber tanta ebullición, o al menos yo no la he detectado, una capa por encima de eso. Está bien tener un método de producción iterativo-incremental, está bien ofrecer al cliente (que muchas veces no es el usuario final, desgraciadamente) un proceso súper adaptable a sus cambios de humor y requisitos… pero estaría mejor dotarnos de los procesos, que tiene que haberlos, para utilizar este gran método para crear el producto que el usuario final necesita. Para ello no basta con que el equipo de desarrollo sea ágil, hay que aprovechar esa agilidad para que los equipos de marketing y ventas (o el product owner, o los encargados de desarrollo de cliente, o quien sea) puedan, a su vez, realizar sus propios experimentos, implementar sus propias métricas y tener sus propios objetivos iterativos e incrementales que nada tengan que ver con el desarrollo y sí con el negocio. Por eso, echo de menos, conceptos que Ries maneja en su libro como “indicadores vanidosos”, “indicadores accionables”, “análisis de cohortes”, “contabilidad de la innovación“… conceptos sin los que scrum, tdd, los sprints y todo lo demás no sirven más que para tener un proceso de desarrollo flexible y adaptable… lo cual no tiene porqué evitar que se acabe construyendo de forma eficiente y completamente flexiblemente un producto totalmente inútil. Supongo que, en España, el principal problema es precisamente que no existen muchas start-ups y hay, sin embargo, demasiadas factorías de software… y en una factoría de software es difícil vislumbrar nada más allá de lo técnico (por no hablar ya de meter baza) y suficiente se tiene con lidiar con el cliente.

Frogtek no es una software factory pero también hemos sufrido en parte ese problema. Tras varios años de trabajo teníamos un equipo de tecnología muy “agile” y muy “kaizen” que implementaba las “ocurrencias” de la empresa de manera eficiente y puntual. Ocurrencias, dicho en el mejor de los sentidos y con todo el cariño, que trataban de ser lo más meditadas posible y que suponíamos beneficiosas para los tenderos y que, lo fueran o no permanecían en el producto sine die. El problema de esta aproximación es que no es todo lo científica que debería ya se basa en asunciones que habría que comprobar, pero que rara vez se comprueban, por falta de tiempo y de proceso principalmente. No basta con implementar User Stories por muy bien definidas que estén, el cementerio está lleno de productos técnicamente impecables. Para solucionar esto en Frogtek vamos a utilizar una capa más de abstracción, basada en Épicas, que iría sobre los sprints de User Stories. Una Épica no es más que un objetivo del product owner para un sprint determinado. Algo así como el objetivo estratégico de alto nivel de mejorar el indicador X de la empresa en un x% (p.e. “hay que conseguir que el 80% de los tenderos levanten su inventario en menos de 2 horas”). Muchas veces, este tipo de mejoras no es cuestión de una sola User Story es, más bien y por limitarlo al producto, un conjunto de historias (“el producto debe adivinar los precios”, “el producto debe adivinar los costes”, “el producto debe proponer los productos más comunes”, “el interfaz debe cambiar así o asá”…) que se pueden desarrollar durante un sprint (de forma ágil, claro), la diferencia con nuestra aproximación pasada es que terminado el sprint el trabajo del product owner no ha hecho más que empezar y a la vez que se empieza a pensar en el sprint siguiente, tiene que echarle un ojo al indicador asociado a la Épica del sprint pasado con el objeto de aprender si los cambios en el producto están teniendo el efecto deseado en el comportamiento real de los usuarios o, por el contrario, no han servido para nada y se necesitan más cambios (volver a iterar).

 

El trabajo del product owner se complica, ya que como se ve en el dibujo el contenido del sprint N empieza a analizar incluso antes de que empiece el sprint N-1.  La secuencia es la siguiente:

  1. Recoger indicadores.
  2. Analizar indicadores y Épicas en evaluación para decidir los objetivos (creando nuevas o modificando Épicas).
  3. Desglosar las Épicas en historias de usuario.
  4. Preparar la historias de usuario (mock-ups, pruebas de usabilidad con clientes, más mock-ups…).
  5. Estimar historias de usuario.
  6. Ejecutar el sprint (incluyendo pruebas, instalación en friendly users, etc).
  7. Instalar nueva versión en usuarios.
  8. Vuelta a 1.

Normalmente los indicadores a recoger y analizar necesitan un tiempo hasta que se generan, al menos en nuestro caso, así que no será extraño que las Épicas que se evalúen en un momento dado se hayan implementado hace dos, tres o más sprints. Por lo tanto el PO, no sólo tiene que estar pendiente del sprint actual sino que tiene que pensar en las Épicas (objetivos estratégicos del producto) que querrá definir para el sprint siguiente, mientras que monitoriza meticulosamente los indicadores asociados a las Épicas que están ahora mismo en evaluación y que pueden seguir así por uno o varios sprints. Porque una Épica, como una User Story, tiene un ciclo de vida ¡e incluso su propia tabla! (se proponen, se discuten, se diseñan, se implementan, se evalúan y si están bien se archivan, muy propio para una tabla de kanban) pero a diferencia de una User Story la evaluación no depende de factores meramente técnicos, si no que es función del valor que aporte al usuario final (si está bien definida y si se monitoriza usando datos reales). El paralelismo con las User Stories también tiene otras ventajas, se puede medir el throughput en Épicas de un equipo, es decir, la velocidad de la empresa para poner hipótesis a prueba y aprender de la realidad, concepto directamente relacionado con las posibilidades de la empresa de sobrevivir.

Al final como en muchos aspectos de la vida, se pueden extraer unas cuantas enseñanzas muy interesantes: No llega más lejos la empresa que hace más User Stories, sino la que hace User Stories como consecuencia del aprendizaje validado de otras User Stories. Es importante tener un framework de gestión del trabajo ágil, pero más importante es integrar en el proceso de producción herramientas de medida como Google Analytics o Flurry que permitan obtener información sobre la utilización de las distintas funciones y que permitan discriminar entre distintos grupos de usuarios con distintas versiones para poder relacionar causas y efectos usando el análisis de cohortes (¿a los usuarios de la versión x+1 les ha ido mejor que los de la version x?). Así que ahí estamos en Frogtek, peleándonos, como buenos novatos, con Analytics y con nuestras propias métricas, discutiendo las métricas que debemos aplicar a las Épicas y tratando de encajar su ciclo de vida con el de definición e implementación de los sprints. Este post, más que una realidad, es una declaración de intenciones acerca de lo que queremos hacer para conseguir ser una “agile data-driven company” para lo cual es necesario ser ágil pero, visto lo visto, no suficiente.

Scrum, Kanban y la matriz de Eisenhower

Érase una vez un Scrum Master que controlaba el proceso Scrumban que un equipo seguía. ¿Le pitan a alguien los oídos?. Dicho de otro modo, para que no se diga que aquí sólo escribimos para los “talibanes agilistas” ;P.

Erase una vez un gestor de un equipo encargado de llevar a cabo tareas planificadas y no planificadas en un periodo de tiempo determinado y cíclico. No sé cual de las dos introducciones es peor.

Érase, también, una vez un Product Owner malvado que no hacía más que meter Kanban en el sprint, haciendo sufrir al Scrum Master y poniendo a prueba la velocidad del equipo. El Scrum Master le insistía al equipo para que no dejara abandonadas las tareas de Scrum, mantuviera una velocidad de Scrum aceptable y consiguiera los objetivos del sprint. Y en estas llegó la retrospectiva y el equipo le afeó al Scrum Master que priorizara el Scrum frente al Kanban, ya que todos son hijos de Dios (del Product Owner en este caso). Así que el Scrum Master se puso a pensar y sacó las siguientes moralejas además de escribir este post.

  • El equipo tiene razón, en teoría, el Kanban es tan válido como el Scrum. La suma de todo nos da la velocidad total del equipo y si los puntos totales al final del sprint son iguales o mayores que los planificados el equipo habrá cumplido con su papel.
  • El Scrum Master también tiene razón, en la práctica, el Product Owner siempre, salvo cambios grandes de requerimientos, espera que todo el Scrum esté acabado en la fecha final de sprint. Si el Scrum se termina, el Scrum Master habrá cumplido con su papel.
  • El hecho de no tener definidos claramente las prioridades del Kanban (FIRE, PRIO, ASAP) puede fomentar estos problemas. El Kanban en Frogtek entra directamente en la pila de producto del sprint y se empieza antes o después en función de lo arriba que entre en la columna. Es decir, es priorizado junto con lo demás (hasta ahí bien), pero luego cuando entra y navega por las diferentes fases no tiene ni más, ni menos prioridad que el resto de lo que está en desarrollo. De ahí que el Scrum Master en algún caso acabe defendiendo lo planificado y el programador quiera que todo se valore por igual, ambos defendiendo su trabajo. Pero ¿debe hacerse?.

Aquí es cuando entra en juego la relación entre las tareas de Scrum, Kanban y la matriz de Eisenhower. Eisenhower se ve que era un tío muy organizado y las malas lenguas dicen que inventó el típico plano cartesiano con cuatro cuadrantes, con un eje señalando la importancia y otro la urgencia. Así tras analizar una tarea en un momento dado podía decidir qué hacer con ella:

  • Urgente e importante. Consejo de Eisenhower: ¡Hazla ya!. Consejo ágil: Normalmente esto es Scrum (en el peor de los casos esto entrará como Kanban de alta prioridad a mitad de sprint pero en ese caso es bastante fácil poner al Scrum Master y al equipo de acuerdo en lo importante que es sacarla la tarea cuanto antes adelante).
  • No urgente pero importante. Consejo de Eisenhower: Planifícala. Consejo ágil: Vuelve a analizarla en la siguiente ronda de planificación y métela como Scrum en el siguiente sprint cuando ya sea urgente e importante.
  • Urgente pero no importante. Consejo de Eisenhower: Delega. Consejo ágil: Normalmente esto es Kanban, ya que su urgencia suele salir a relucir a mitad de sprint y nunca tendrá la importancia suficiente para entrar como Scrum. Este tipo de tareas son las que crean conflicto a veces y las que acaban retrasando los sprints.
  • Ni urgente, ni importante. Consejo de Eisenhower: ¡A la basura!. Consejo ágil: si ves muchas de estas en tu tabla, cambia de Product Owner.

Simplificando un poco se podría asimilar Importante con Scrum y Urgente con Kanban. Es por eso que como Scrum Master creo que Scrum casi siempre debe ir por delante que Kanban. Siendo flexibles, claro. 🙂

Carlos Ble Tour 2011: Primera parada Frogtek

Pues sí. A principios de diciembre Carlos Ble anunció una gran iniciativa, se ofrecía a trabajar un par de días en distintas empresas que le pillaran a mano simplemente a cambio de los gastos de manutención y parte del viaje. Por aquel entonces ya estábamos en contacto con él por que en Frogtek teníamos la ilusión de que Walqa acogiera su curso de TDD, así que nos faltó tiempo para ofrecernos a ser la primera parada de un viaje que seguro va a ser muy provechoso para las empresas que visite y para él. Es un win-win claro (sin tecnología de Redmon de por medio). Para Carlos tiene que ser muy enriquecedor visitar distintas empresas, con distintos productos, distintas metodologías, problemas y personas. Para las empresas es una gran oportunidad de someterse al escrutinio de alguien externo con gran conocimiento de la tecnología y el eXtreme Programming. Fuimos los más rápidos, así que aunque el curso de TDD al final no se llevó a cabo en Walqa, sino en Zaragoza, al acabar Carlos tomó rumbo al norte y recaló en nuestras oficinas por un par de días que se nos hicieron realmente cortos.

El proceso empezó el mismo miércoles por la noche cenando en el Café del Arte una pizza que estaba muy buena pero que era muy difícil de cortar. Hablamos largo y tendido sobre Frogtek y MavenCharts, las fases por las que habíamos pasado, los logros y dificultades que había encontrado por el camino. Me temo que monopolicé un poco la conversación, el problema es que cuando me pongo a contar la historia de Frogtek puedo estar horas y horas y además quería aprovechar los dos días siguientes a tope y para ello tenía que ponerle al día de demasiadas cosas. Mucha información en muy poco tiempo, me temo. 🙂

El jueves empezó la movida. Primero ponte a explicar la tabla de Kanban y date, enseguida, cuenta de que hemos ido complicando las cosas poco a poco, hasta el punto de que explicar el funcionamiento de una simple tabla deja de ser algo trivial. El tiempo dirá si las sucesivas evoluciones de nuestra metodología han sido para bien o hemos complicado demasiado el proceso. De momento estamos bastante satisfechos. Después tuvimos nuestro stand-up (un poco tarde, me costó 1€ que pagué bien a gusto). Decidimos, en honor de nuestro invitado, que no íbamos a hacer nada especial por su visita así que le obsequiamos con nuestro habitual spanglish. Mr. Ble aguantó el tipo (y la risa) sin problemas así que al final del stand-up accedimos a rellenar un pequeño formulario de preguntas que pretendía chequear la “salud” de nuestro grupo y nuestras expectativas ante su visita.

Lo siguiente fue hablar sobre nuestro sistema de integración continua basado en HUDSON y que Carlos le echara un primer vistazo a la realidad de nuestras baterías de tests. Mucho test de integración pero poco unitario, también algunos funcionales pero todos mezclados y poco ágiles. Es difícil hacer TDD si los tests unitarios tardan varios minutos en pasar. Diagnóstico: grandes dosis de buena voluntad pero mucho por mejorar.

Carlos y Alberto peleándose con el GAE

El resto del jueves y viernes tocaba Pair Programming. Primero Javier Linares y nuestro proyecto para Android TiendaTek, luego Alberto Gualis y los tests de lado de servidor en Python y sobre Google App Engine. Tanto Javi como Alberto pueden dar su propia opinión pero sin duda fueron unas horas muy provechosas durante las que pudimos entender como evolucionar nuestra arquitectura y discriminar y organizar nuestros tests para hacer un TDD realmente útil y no “de postal”.

También hubo tiempo para revisar el código de Linares y Carlos el viernes por la mañana.

Al filo de las 3 de la tarde del viernes acabamos la sesión con una retrospectiva que nos reveló distintas cosas:

  • Que dos días escasos habían sido muy cortos para todo lo que nos hubiera gustado aprovechar.
  • Que podríamos mejorar nuestro nivel de calidad pidiéndole a alguien externo pero cercano que usara nuestro producto (¿quizá la tienda de ultramarinos de Cuarte?, ¿la cafetería de Walqa ;)?…)
  • Que nuestra manera de trabajar podría verse beneficiada del uso de repositorios de código distribuidos (nuestro objetivo de migrar a GIT o similar es como el arco iris, que por mucho que camines hacia él nunca lo alcanzas).
  • Que Eclipse es lento para el TDD.
  • Que deberíamos hacer alguna retrospectiva de código (algo parecido hacemos con nuestra reunión mensual de “Nuestros mejores bugs“, pero no tenemos muchos reparos en ignorar la reunión a poco trabajo que tengamos).
  • Que intentáramos desligar funcionalidad transversal para integrarlo en librerías (estamos en proceso ya que Android solo hace muy poco lo permite).
  • Que el código que nunca falla es el que no existe (por eso yo nunca creo bugs).
  • Y que habláramos y habláramos y habláramos… la (buena) comunicación es siempre la base para que algo funcione (bien).

Y de ahí disparados al Dojo… pero eso será otra historia.


El post-mortem del Ranatón

Volvimos de Morillo. Sanos y salvos y ahora estamos disfrutando todos de unas merecidas Navidades. Aunque, bueno, siempre tiene que haber alguien al pie del cañón, también es cierto que se trabaja muy tranquilo cuando todo el resto del mundo está de vacaciones. Es como un pomodoro continuo.

El Ranatón fue una gran experiencia. El sitio resultó muy apropiado (estábamos solos y aislados), la relación calidad precio fue lo más ajustado que pudimos encontrar y la conexión WiFi, el gran miedo que teníamos, funcionó más que aceptablemente para 9 personas que estábamos continuamente conectados.

El acabar el tercer día hicimos una especie de Stand-up retrospectivo. Sirvió para que cada uno presentara los avances conseguidos; notorios fueron los del equipo “Portal” que nos presentaron toda una serie de páginas web con unas gráficas más que jugosas, menos obvios pero tan o más importantes fueron los conseguidos por el equipo “Inframundo” (de infraestructura) que se dedicó a la no muy agradecida tarea de optimizar nuestra querida TiendaTek. Enhorabuena a los dos equipos.

Las conclusiones del Ranatón fueron las siguientes:

  • A todo el mundo le gustó, sobre todo porque rompe la rutina y es una buena manera de hacer equipo y de que todos nos conozcamos un poco más.
  • Es muy muy útil hacer una sesión intensiva en la que PO y programador tengan conexión directa y puedan trabajar juntos codo con codo. Esta vez sólo teníamos a David, pero ójala en nuestro siguiente Ranatón podamos traer también a Yael, Mark y Kristel. Puede parecer una obviedad pero es que nosotros tenemos a POs y programadores separados por miles de kilómetros en nuestro día a día.
  • Aunque durante un Ranatón se puede llevar a cabo cualquier tipo de tarea es mucho mejor aprovechar para implementar US que necesiten de mucho feed-back por parte del PO y de los compañeros. También es mejor no hacer US que sean supercomplicadas, son preferibles las tareas cortas y también aquellas que tengan un reflejo rápido y grande en el producto. Anima mucho ver que se avanza rápido y que los avances son muy visibles. Esto lo conseguimos con el equipo Portal, con el equipo Inframundo fue más complicado (el pobre Pablo se pegó tres días optimizando listas), lo haremos mejor la próxima vez.

  • Es muy útil tener una pizarra donde apuntar las US y tareas y poder registrar los avances. Nosotros además utilizábamos un timbre (de estos hotel) para dar cuenta de los US terminadas y de los cerditos que iban cayendo.
  • No fue el típico kick-off para hacer team-building ya que fuimos a Morillo a currar y no hubo las típicas excursiones o actividades lúdicas oficiales. Tres días fue la duración adecuada, acabamos todo bastante cansados.
  • Sí que hubo actividades lúdicas oficiosas como hacer alguna pausa para ir a arreglar el mundo al salón de al lado (el hotel entero estaba a nuestra disposición ya que estábamos solos), echar una o dos cervezas mientras se programa (nada de drunk-programming) o jugar un partido de tenis a dobles en la Wii.

  • La comida fue muy buena pero demasiado abundante, costaba un poco reenganchar después de un típica comida montañesa o de las deliciosas pizzas que tomamos las dos noches en Aínsa. Restaurante con loro incluido, por cierto… muy apropiado para el señor Stallman.
  • Trasnochar trabajando es un arma de doble filo. Por un lado es divertido y todo un reto estar programando hasta las 3:30 de la mañana. Cuando a partir de cierta hora el ambiente se relaja y se pone música de fondo para todos o se comparten unas cervezas uno tiene la impresión de estar una start-up al más puro estilo americano (los que hayan visto “La red social” que no se lleven a engaño, lo nuestro fue mucho más tranquilo… ni sexo, ni drogas). Por otro lado el oficio de programador tiene mucho de artesano y cirujano y, por lo tanto, la falta de sueño no es buena compañera y los grandes alardes se pagan de un día para otro… aquí como en muchas otras facetas de la vida lo importante es la regularidad y la constancia.
  • Concentrar a todos los “roncadores” en la misma habitación hizo que el resto del equipo descansara a pierna suelta… pero condenó al insomnio a buena parte de los primeros en la “habitación del pánico“.

Y esto es todo. Felices Navidades a todo el mundo.

El Pomodoro Síncrono ya está aquí

Ayer tuvimos una reunión de retrospectiva. Solemos hacer una al mes aproximadamente. Ésta fue particularmente interesante, especialmente por Jose, Linares y Pedro que habían acudido el fin de semana anterior al Agile Open Spain de Barcelona y se trajeron feed-back fresquito e interesantes ideas para aplicar en nuestro entorno de trabajo.

Esta semana presentamos… ¡el Pomodoro Síncrono! (lo que hay que oir). Se trata de buscar maneras de aumentar la productividad y se puede aplicar tanto a programadores como a cualquier otra persona que necesite cierta tranquilidad y concentración para llevar a cabo su trabajo. Básicamente la idea del Pomodoro consiste en reservar espacios de tiempo para aislarse del entorno y concentrarse un 200% en una tarea. Está claro, si apagas el teléfono, el chat, el twitter, el facebook, haces voto de silencio y te pones unos cascos tu productividad aumenta varios órdenes de magnitud. La duración de dichos espacios de tiempo es variable y personal, hay quien es capaz de aguantar en una especie de estado entre el trance y el nirvana durante varias horas seguidas, hay quien con media hora de aislamiento sensorial tiene más que suficiente. Resulta obvio que no es una técnica de la que se deba abusar pero también que, cada vez más y sobre todo en un trabajo como el de programador, si se quiere aumentar la productividad (esto es, ser más rápido, cometer menos errores y acabar más cosas) hay que ser capaces de abstraerse del ruido, principalmente spam cibernético, que nos rodea.

¿Y por qué Pomodoro?. Porque el tío que lo inventó usaba un reloj de cocina con forma de Tomate. ¿Y por qué Síncrono?. Porque si lo práctica toda la oficina a la vez es mucho más efectivo. Así que hemos decidido hacer Pomodoro Síncrono todos los días:

  • de 9:30 a 10:30
  • de 12:00 a 13:00
  • de 16:00 a 17:00

Desgraciadamente no hemos encontrado un reloj-tomate… así que siempre nos quedará el consuelo de decir: “Hoy he currado un huevo”.     😛

Crear nuestros propios adapters para listados, y no morir en el scroll

Si no vienes del post introductorio, quizás te gustaría leerlo.

Tercera entrega de la optimización a la que fue sometida nuestra aplicación. Esta vez, tiene que ver con los listados de elementos; de muchos elementos, como puede ser el inventario de una tienda. Sirva como ejemplo práctico que solíamos crear nuestros propios adapters (extend BaseAdapter, todo sea dicho) muy alegremente, pero sólo al principio. Como buenos desarrolladores, copiábamos código del primer sitio que encontrábamos, sin fijarnos en lo óptimo que era. Y como buenos desarrolladores, repito, hacíamos las pruebas de nuestros propios adapters con 5 o 6 elementos para simular el inventario de una tienda.
¿Qué pasó? Cuando tuvimos nuestro primer tendero con ganas de inventariar toda su tienda, enseguida se nos vio el plumero. Una lista de 1200 (mil doscientos) elementos, con código copi-pegado y poco probado, tiene un 99% de posibilidades de ir más lenta, al hacer scroll, que el stand-up meeting de un lunes de resaca.

Como bien apuntó Francho en un comentario, una manera de ganar velocidad haciendo scroll es omitiendo los reiterados findViewById cada vez que hay que cambiar los valores del famoso convertView. En su lugar, debemos hacer uso de los tag que podemos asignar a las filas (rows) de nuestro listado. ¿A quién no le suenan las funciones setTag() y getTag()? Pues hace tiempo, a nosotros ni se nos pasaban por la cabeza. Luego de ver el magnífico video de la Google I/O 2009, Making your Android UI Fast and Efficient, hicimos una capillita en la oficina a Romain Guy. Merece la pena cada año ver los videos de la Google I/O…

Muy bien. Con esto que he nombrado arriba, tenemos solucionado una parte de nuestro problema; es decir: que cada vez que se haga scroll y haya que buscar los widgets cuyas propiedades han de cambiar, no se haga siempre mediante el findViewById(), si no que utilicemos los tags.
La otra parte del problema, venía al experimentar que aún habiendo mejorado la velocidad de scroll un poquito, no conseguíamos ese efecto de fluidez que una lista ha de tener. ¿Por qué? Muy sencillo: cada nueva fila que aparecía en el listado al hacer scroll contenía uno, o a veces más, widgets cuyo contenido era calculado en el mismo momento del getView(). A veces eran cálculos de sumas, fáciles. Otras veces eran consultas a la Base de Datos, lo cual nos estaba robando mili-segundos de muy alto precio al hacer que el scroll de tu aplicación sea fluido.

Por lo tanto, otro mandamiento que nos grabamos a fuego en la mente: No harás consultas a la Base de Datos en el getView() de tus propios adapters. Las consultas, se hacen antes del setAdapter() del ListView. Si no, arderás en el infierno de los developers.

Y ya, para rizar el rizo de la optimización de un listado y hacerlo más rápido que Usain Bolt: Dime cuál es la fuente de datos que alimenta tu Adapter, y te diré cuántos mili-segundos perderás en cada getView().
¿Por qué digo esto? Por que, como bien dijo Pedro en un post: hay que evitar a toda costa el trabajar con ArrayList de objetos propios en android. Reflection lo llaman, ¿no? Android y reflection no son buenos amigos.
Así que en lugar de acceder a los datos que poblarán nuestros ListView de manera limpia y con código legible, como puede ser un ArrayList, os recomendamos que uséis directamente el objeto Cursor devuelto por la Base de Datos. Sí: mucho más sucio el código, pero no veas lo rápido que va todo.

En resumen, hoy hemos visto:

  1. El ViewHolder para evitar millones de findViewById()
  2. No hacer consultas a la Base de Datos dentro de un getView()
  3. La fuente de datos de un Adapter, a ser posible, que sea un Cursor

¿Me olvido algo?

El findViewById(), y lo cargante que puede llegar a ser

Si no vienes del post introductorio, quizás te gustaría leerlo.

Segundo asalto. La aplicación sigue estando lenta, y no sabemos muy bien por qué. Ya no hay tantos onMeasure(); sólo los “necesarios”. Pero hay una nueva función que aparece en los primeros puestos de nuestro querido traceview: findViewTraversal(). ¿Y quién llama tantísimas veces a esta última? pues findViewById().
Y es que esta función, es la que enlaza un recurso de la interfaz de usuario de una aplicación, con una variable en nuestro código. O simplemente se encarga de acceder a ese recurso para cambiar una de sus propiedades.

Supongamos, pues, que tenemos un layout con una veintena de widgets entre layouts, Buttons, EditText, TextView, ImageView… Y que en un momento dado del ciclo de vida de la actividad, tenemos que acceder a uno ó varios de ellos para cambiar sus propiedades. Podemos acceder a ellos del modo:
((EditText) this.findViewById(R.id.edittext_cliente)).setOnClickListener(…);
Aunque también podemos guardar el valor que devuelve la función findViewById() en una variable, o variable miembro de la Activity para operar con ella varias veces a lo largo del citado ciclo de vida.

¿Cuál es la diferencia? y ya que estamos ¿cuál era nuestro error? Pues que cada vez que se llama a la función findViewById(), android recorre todo el árbol jerárquico del Layout que hemos definido previamente en XML, en busca del recurso al que queremos acceder. Como ya habréis averiguado, el costo de esa operación es muy alto si el control a encontrar es de los últimos en el árbol, o si hay muchos niveles de layouts anidados, o simplemente queremos cambiar el valor de una de las propiedades de ese recurso bastantes veces a lo largo del ciclo de vida de la aplicación.
Si a esto le sumamos que el cambio en una de las propiedades de un EditText (por ejemplo), afecta a cinco o seis widgets más; y a todos accedemos del modo findViewById(), estamos vendidos.

La solución para una Activity, cuyos widgets han de ser modificados con bastante asiduidad es el guardar el resultado de la función archinombrada en este post en variables miembro, a las cuales se accederá sin tener que recorrer (findViewTraversal()) todo el árbol jerárquico “n” veces, con la consiguiente pérdida de velocidad en la transición de un estado a otro de la Activity.
Eso sí: Podemos prescindir de estas variables miembro si accedemos una, y sólo una, vez al widget en cuestión.

Esto también hay que grabarlo a fuego en la mente.

Diseño de layouts XML, sucios pero eficientes, en android

Si no vienes del post introductorio, quizás te gustaría leerlo.

Bien, pues a medida que añadíamos nueva funcionalidad a la aplicación en la tablet, experimentábamos una lentitud tremenda en alguna de las actividades que abríamos. No era normal que si en el teléfono, el mismo layout funcionaba perfectamente, ¿por qué al unir dos layouts en uno sólo, pero más grande, vaya tan lento? Respuesta: no hay nada que el traceview no solucione. Nada.

Rápidamente, Julio comenzó a ver que había miles de llamadas a una función nativa de android que se llama onMeasure(). La misma, se encarga de que cada vez que hay que posicionar un widget dentro del layout, hacer las medidas necesarias de lo ya presente en el mismo para así determinar dónde debe de ir colocado. ¿Y qué problema hay con esto? Pues que si tu layout tiene muchos widgets, y estos widgets tienen algunas de sus propiedades (como el layout_width, o layout_height) con valores wrap_content o fill_parent, todo resultará en un festival de onMeasure(). Creo recordar, que el 90% del tiempo de carga de una pantalla ineficiente nuestra, se lo llevaba esta función. Obviamente, pasamos de medio segundo para cargar una pantalla determinada en el teléfono, a 2 ó 3 (incluso más) en la tablet.
Por lo tanto, uno de los nuevos mandamientos que hay que grabarse a fuego es que: siempre darás medidas a tus widgets (Button, TextView, Layouts, EditText, etc), en lugar de que las decida android. Siempre, a no ser que sea estrictamente necesario; que alguna vez lo es.
Este es el primer paso para que la aplicación fluya un poquito más rápido que antes. Pero sólo un poquito.

¿Qué, que aún no hemos ensuciado el layout? Bien, pues este punto tiene que ver con el nivel en que un widget se encuentra anidado. Podemos tener un layout muy sencillo, con un RelativeLayout y dos Button dentro, o podemos tener algo como esto (o peor). Es por esto lo que un día dije en la oficina. Somos unos barrocos.
Por lo tanto, desde frogtek recomendamos encarecidamente que, a poder ser, se tenga un RelativeLayout padre con todos sus hijos al mismo nivel. Si se tienen 4 o 5 widgets en el layout, serás afortunado; si tienes 30 o 40 como en alguno de nuestra tablet, puedes volverte loco buscando uno en concreto en el Outline de eclipse. Cierto es que queda todo mucho más ordenado si vamos anidando widgets dentro de Layouts, pero se pierde rapidez al cargar, ya que también hay muchos más onMeasures() que si estuviesen todos al mismo nivel. Y traceview no miente.

Por lo tanto, el primer paso para agilizar la carga (o recarga) de una pantalla, pasa por dar medidas a todo widget al que podamos dar medidas absolutas, e intentar que nuestros layouts sean lo menos barrocos posibles en cuanto a ordenación jerárquica se refiere.

Consejo extra que se me ocurre mientras escribo: si utilizas el LayoutInflater para cargar layouts dentro de otros, tener un layout con uno o dos niveles de anidación ayudará mucho. Nosotros, que tenemos layouts dentro de layouts, y de nuevo dentro de layouts (por necesidad), lo hemos notado.

Los 10 mandamientos de Java

Quizás todavía hemos hablado poco de Sonar, pero es necesario indicar que se trata una de las herramientas indispensables a la hora de crear código elegante. Básicamente, esta utilidad nos permite analizar nuestros proyectos en busca de código poco eficiente o mejorable, ordenando estas infracciones (suena mejor que violaciones) por su gravedad (blocker, critical, major, minor e info).

Con esfuerzo, hemos conseguido eliminar todas las infracciones críticas de nuestros proyectos, pero como sabemos que volveremos a tropezar dos veces con la misma piedra, necesitábamos algún tipo de recordatorio que nos permitiera tener siempre en mente el no volver a cometer esas infracciones.

De ahí surgió la idea de crear una tabla con los diez mandamientos de Java. Aquí tenéis la versión final. Explicaré cada punto en detalle.

Los 10 mandamientos de Java
I. Thou shall not access non-static variables in a static manner.

Esta infracción se repetía bastante en nuestro código. Si tenemos un método estático , no debería poder acceder a variables que no están pensadas para llamarse de forma estática. El compilador lo permitirá, claro, pero manda al garete la filosofía de los métodos estáticos.

II. Do not write a static variable from an instance method. Ever.

Parecida a la anterior, pero al contrario. Si tenemos una variable estática, no deberíamos poder escribirla desde un método perteneciente a un objeto instanciado. Sirva de ejemplo:

private static String mTitle;
...
public void showDialog(String title, String message) {
   mTitle = title;

III. Thou shall not leave empty if-else/switch statements.

¿Cuántas veces habrá pasado? Dejamos un todo en el if y asunto resuelto. Pues a partir de ahora, pecado mortal.

if (item != null) {
   newPurchase();
} else {
   // no purchase yet
}

Aunque no lo pone, esto también se aplica a los bucles vacíos.

IV. Thou shall not repeat code in a if-else/switch statement.

A veces por dejadez, mantenemos dos bloques de un condicional exactamente iguales. Veamos un ejemplo con un switch:

switch(gafas){
   case 1: return "de lejos"; break;
   case 2: return "progresivas"; break;
   case 3: return "del cerca"; break;
   default: return "de lejos"; break;
}

Podría modificarse quitando el caso 1 o fusionándolo con el default.

V. Thou shall not leave a private method not being called from anywhere.

Sencillo: no creemos métodos que no sean utilizados en ninguna parte. Si el título del metodo tiene un rayica amarilla, mal asunto. Siguiente.

VI. Remember to not null-check a value which is already null.

Si estamos seguros al 100% de que algo tiene como valor null, no tiene sentido hacer una comprobación. Lo mismo para el caso contrario: si es imposible que una variable tenga valor null, resulta redundante el comprobarlo.

VII. Thou shall not use short variable names.

Pecadores todos aquellos que utilizáis nombres de variables como “cursor c”, “RelativeLayout rl” o “String s”. Quedan excluidas las variables de iteración dentro de un bucle.

VIII. Do not take the visibility of variables in vain

Qué bonito sería el mundo si todas las variables fueran public. Nos ahorraríamos más de un disgusto. Pero si una variable sólo se utiliza dentro de el entorno de una clase, debemos declararla como private, o arder en el infierno. Lo mismo con protected, claro.

IX. Remember to write javadocs and keep it holy.

Método que dejamos sin Javadoc, 10 flagelaciones.

X. Avoid duplicate literals, if it is repeating, create a variable.

if (numPiscinas == 1) {
   show("mensaje", 1);
} else if (numPiscinas == 2) {
   show("mensaje", 2);
} else if (numPiscinas >= 3) {
   show("mensaje", 4);
}

Si la cadena “mensaje” es usada en el código continuamente, mejor escribir una variable propia con la cadena, que seguro que nos resuelve la vida.

Y eso es todo. Podemos crear código sin hacer caso de estos mandamientos, y obtendremos un buen resultado. Pero seguro que si los seguimos a rajatabla, adquirimos unas costumbres de programación muy saludables. Por supuesto, estos puntos son cuestionables, no se trata de los más críticos, sino simplemente de los que más suelen repetirse en nuestro código. Os animo a que elaboréis vuestra propia lista de mandamientos y la colguéis bien alta para tenerla siempre en mente. Y para acabar, me despediré con una frase de Charles Reade:

“Siembra un acto y cosecharás una costumbre. Siembra una costumbre y cosecharás un carácter. Siembra un carácter y cosecharás un destino.”

Eficiencia en Android

Continuamos con nuestro afán por tener una aplicación más eficiente e iremos escribiendo con lo que nos vamos encontrado: tenemos una tabla con datos para sincronizar y mientras se sincronizan actualizamos una barra de progreso en la status bar. Actualmente, actualizábamos la barra de progreso cada vez que se procesaba una fila, con lo cual, actualizábamos la barra de progreso tantas veces como filas teniamos.

Julio (QA) al percatarse de que esta operación costaba mucho tiempo, lanzó su herramienta más preciada…, el traceview. Se pasó por mi mesa y vimos que la clase Notify de Android se nos estaba llevando un gran tiempo debido al problema explicado en el párrafo anterior.

Con lo que nos pusimos manos a la obra y escribimos el correspondiente método estático que nos calcula el porcentaje y nos dirá si tenemos que actualizar la progress bar, de este modo siempre la actualizaremos 100 veces. Ahí va:

/*** This method retuns true if an update is needed in the progess bar
 *
 * @param itemsCount The total number of items
 * @param iCount The actual item to be processed
 * @return True if is necesary to update the progress bar
 */
 public static boolean isNeededProgressBarUpdate(int itemsCount, int iCount) {
    if (itemsCount != 0 && iCount !=0) {
        int perActual = (iCount * 100) / itemsCount;
        int perPrevious = ((iCount - 1) * 100) / itemsCount;
        if (perActual == perPrevious) {
            return false;
        } else {
            return true;
        }
    } else {
       return true;
    }
 }

Por otro lado también hemos notado que el Log de Android consume bastantes recurso y así lo comentan en la documentación. Pero no tenemos muy claro qué pasa con el Log.d (DEBUG). Según dice en la documentación, no se escribe en tiempo de ejecución, pero… creemos que depende de si marcas el teléfono para debug o no.

Como conclusión final hemos conseguido reducir un proceso que le costaba una hora a unos 20 minutos.