lunes, 18 de julio de 2011

La memoria y TDD: Refactor/test backlog & Sleeping in Red

Hace tanto tiempo que no escribo en este blog, y ahora vuelvo a escribir (sin razon aparente) para contarles unas impresiones acerca de como se me ocurre optimizar TDD en relacion con la memoria (humana), en esto cualquier comentario que puedan aportar no solo sera bienvenido sino que tambien muy apreciado

Lo basico

Bueno, para no dar mas vueltas voy directo al tema empezando por la basico (salteen este parrafo si saben basicamente en que consiste TDD)
TDD es una practica de programacion que basicamente consiste en escribir los tests primero y refactorear el codigo convenientemente, este proceso se ejecuta de manera iterativa pasando por tres fases en cada iteracion: rojo, verde y refactor. A grandes rasgos:
  • La fase "roja" consiste en escribir tests que fallen expresando especificaciones de como las cosas deberian funcionar, antes de que se implementen
  • La fase "verde" consiste en buscar que esos tests pasen a toda costa, es decir que "vale todo" de manera que el codigo se llena de chanchadas en esta fase (lease codigo duplicado, harcodeos, variables globales y cualquiera de esas cosas feas que se ven en las pesadillas)
  • La fase de refactoring la cual hace mas lindo el codigo, limpiando todo lo que se ensucio en la fase verde anterior mientras se mantenga el 100% de tests en verde
Para mas informacion de TDD pueden ver el articulo de la wikipedia o simplemente buscar en la web que esta lleno de blogs que explican cosas muy interesantes

¿Que tiene que ver la memoria con TDD?

La memoria tiene que ver con todo, y en el caso especifico de TDD, la memoria del programador actua diferente de fase en fase (suponiendo que no use un soporte externo como voy a explicar mas adelante).
  • En la fase roja, que inicia el ciclo de TDD, la informacion que el desarrollador utiliza para escribir los tests son las especificaciones (para reducirlas a especificaciones de componentes en el codigo, etc...). No se necesita ninguna informacion memorizada ya que las especificaciones vienen escritas (y si no, ya no es un asunto de TDD). Solo se requiere informacion extra para escribir casos "borde", caminos "no felices" o cualquier cosa que no este explicitamente en la especificacion.
  • En la fase verde, se tiende a usar informacion que se escribe en los tests para hacerlos pasar, como por ejemplo nombre de clases, metodos u otra informacion que se escribio en los tests. Depende del caso, se suele utilizar mucho conocimiento del proyecto, pero por ahora eso no importa porque esa memoria esta mas alla del scope de un ciclo de TDD
  • En la fase de refactor, se utiliza informacion que esta en el codigo (por ej, al observar metodos duplicados) e informacion producto de ideas, razonamientos, observaciones al codigo efectuadas en cualquier momento, ya que el codigo se esta observando siempre y hay que considerar que refactorear codigo para mejorar su calidad interna no es una tarea trivial (como si lo es hacer que los tests pasen) y requiere mas trabajo mental
Las fase que mas informacion producida en el mismo ciclo y mas trabajo mental requiere es la de refactor, la fase verde requiere mas trabajo mental que la roja

Refactor backlog

El que haya usado TDD, seguro se encontro en la situacion de duplicar cierto codigo para hacer que un test pase teniendo en mente refactorizarlo despues (no en el momento), no deberia memorizarse esa informacion ya que eso es proclive a olvidos y disminuye la concentracion en la tarea del momento, en lugar de eso es mejor usar un "backlog" (que seguramente no sera el mismo backlog agile, es algo distinto) o un "ToDo list" el cual se debe alimentar cada vez que se observe algo que deba ser refactoreado y que no corresponde hacerlo en ese momento

Test Backlog

Para aplicar el mismo concepto que en el parrafo anterior, cada vez que miramos al codigo, se nos ocurre "¿y si llamara a tal metodo con tal argumento, fallaria?", lo mejor en ese caso sera escribir un test que pruebe eso. No suena muy practico interrumpir el trabajo para hacer pasar los tests o el trabajo de refactoring para escribir el nuevo test fallido en el momento, por eso es mejor anotar los test nuevos en algun lugar a medida que se van descubriendo e implementarlos mas tarde. En mi opinion, lo ideal es hacerlo al finalizar la fase verde o de refactor con todos los tests pasando (se considera que se vuelve a la fase roja)

Dormir en rojo

Todos tenemos que descansar en algun momento, y el verdadero descanso consiste en distraccion total y si es posible completo olvido del trabajo (y si es posible irse a dormir :P). Eso implica que, al volver al trabajo, tratar de recordar informacion acerca de la tarea que se estaba realizando antes de interrumpirla para descansar, es significativamente mas costoso que recordar lo que se estaba haciendo hace pocos minutos o segundos (como en el caso del trabajo continuo).
La fase que menos memoria reciente requiere (despues de la inicial) es la verde (la de hacer pasar los test), por eso si se va a interrumpir la tarea lo mas conveniente es hacerlo con todos los nuevos tests fallando (al terminar la fase roja) ya que si se hace antes del refactor al volver se tendra que recordar los items de refactor pendientes (a pesar de usar el backlog, este solo actua como un ayuda-memoria que hay que leer, no como una memoria auxiliar). La fase roja inicial requiere menos memoria reciente pero es mas trabajosa, usa informacion de las especificaciones y es mucho menos trivial por lo que aunque tambien sea util interrumpir antes de escribir los tests no es tan conveniente como pausar despues de escribirlos.

Conclusiones
  • Voy a asegurarme de escribir todos los test que pueda antes de interrumpir la tarea para descansar, y despues hacerlos pasar cuando vuelva a trabajar
  • Voy a implementar un backlog/ToDo con items de refactor que voy a consultar cuando haga pasar todos los test
  • Voy a implementar un backlog/ToDo con items de test (pero voy a ver si eso realmente es util o no)
Links

TDD en la wikipedia: http://es.wikipedia.org/wiki/TDD