Publicidad:
Terra
La Coctelera

Desafío Abredatos 2010

Y tú deberías también.

git bisect

Siempre que quiero usar git bisect, como lo hago de pascuas a ramos, me olvido y lo tengo que buscar en la documentación de git, que si bien es bastante exhaustiva es poco didáctica. Así que aprovecho que me lo estoy apuntando para escribir esta mini-receta por si le puede servir a alguien más.

Para los que no lo conozcan, git bisect es un comando de git para, dados un problema conocido en la revisión actual del software, y una revisión pasada que se sabe que no lo tiene, localizar el commit que introduce el problema usando búsqueda binaria. Un ejemplo de esta búsqueda binaria:

Pongamos que tenemos un problema en la revisión actual (115) que sabemos que no existía en la revisión 100:

  • Git nos lleva a la revisión intermedia (108) para que comprobemos si existe el problema
  • Pongamos que sí existe, se lo decimos y Git nos lleva a la intermedia entre la última buena conocida (la 100 de momento), y la primera mala (108), es decir la 104, para que lo volvamos a comprobar
  • Pongamos que aquí el problema no existe, Git vuelve a repetir el proceso (nos lleva a la intermedia entre la última buena conocida (la 104, ahora), y la primera mala (108), esta vez la 106)
  • Repitiendo el proceso vamos a llegar al commit ofensor

Más esquemáticamente:

Seguro que muchos de vosotros aprendísteis esto en la universidad; a mí me lo enseñó mi padre para buscar cortocircuitos en el Scalextric :-P

Pues bueno, en git eso se hace así:

 $ git bisect start
 $ git bisect bad [commit/branch/tag/etc]
 $ git bisect good [commit/branch/tag/etc]
 

El commit se puede omitir y te refieres al actual. El caso es que con esa información git ya nos lleva a su primer candidato (el commit intermedio). Ahora nosotros deberemos comprobar si existe o no el problema. Dependiendo de lo que se trate, habrá que ejecutar algo, cargar una página en un navegador, lo que sea que reproduzca (o no) el problema. Y entonces, haremos:

 $ git bisect good/bad
 

Git nos hará saltar otra vez al siguiente candidato. Repetimos el proceso y Git acabará por decirnos algo como:

 61abbbfb73e6abb6629f73297656aaf642058d21 is the first bad commit
 

Que era lo que queríamos saber, ahora ya puedes usar tu arsenal habitual para resolverlo (principalmente, git show 61abbbfb73e6abb6629f73297656aaf642058d21 para ver el diff).

Importante acordarse de dos cosas antes de terminar:

  1. Git nos ha dejado en el último commit que hemos probado (que no es ni siquiera necesariamente el commit malo). Según lo que queramos hacer, tendremos que hacer git checkout HEAD o git checkout 61abbbfb73e6abb6629f73297656aaf642058d21)
  2. Git guarda la información necesaria como tags, que ya no necesitamos. Usa git bisect reset para borrarla o la próxima vez que lo quieras usar puedes liarte un poco ;-)

¡Mamá, sin manos!

Lo que de verdad hace de git bisect la releche, es que ese proceso se puede automatizar. Escribe un test de unidad, un script, algo que reproduzca (o no) el problema, y git bisect lo ejecutará por tí las veces necesarias para averiguar cuál es el primer commit que hace tu prueba fallar. Simplemente crea un script (puede valer algo que ya tengas) que tenga código de salida 0 si la prueba pasa y cualquier otro si no (todas las herramientas de testing que yo he usado alguna vez cumplen eso) y (por ejemplo):

 $ git bisect start
 $ git bisect bad
 $ git bisect good wadus
 $ git bisect run spec spec/bisect_spec.rb
 

Si el test es costoso y/o hay muchos commits entre wadus y HEAD vete a por un café, pero vamos, el caso es que cuando vuelvas tendrás en tu pantalla al malo de la peli ;-)

IMPORTANTE:

Ten en cuenta que git va a hacer checkouts para cambiar entre revisión y revisión, así que:

  • No inicies el proceso con cambios sin comitear o los perderás
  • Si vas a usar un test que está en el código del repositorio, también el test cambiará de versión y puede que en determinadas revisiones no reproduzca el problema (o ni siquiera exista). Lo más cómodo es crear uno nuevo y no hacer git add. Una vez ejecutado el proceso puedes considerar añadirlo a los ya existentes o no (según convenga).

Charla sobre GIT el 4 de noviembre en Leganés

El miércoles 4 de noviembre (a las 16:00) estaré dando una charla sobre GIT, el sistema de control de versiones distribuído. La charla tendrá lugar en el marco de las jornadas sobre software libre que viene organizando el GUL-UC3M lo que es De Toda La Vida™ en el campus de Leganés de la Universidad Carlos III, con otro montonazo de charlas interesantes. La charla tendrá una duración de casi dos horas así que dará tiempo a profundizar bastante en el uso diario de GIT, partiendo desde 0. Si llevas tiempo pensando en empezar a usarlo y has conseguido encontrar excusas para no hacerlo, se te acaban de terminar, ¡te espero allí!

They don't understand

The reason most people want to program for the web is that they're not smart enough to do anything else. They don't understand compilers, concurrency, 3D or class inheritance. They haven't got a clue why I'd use an interface or an abstract class. They don't understand: virtual methods, pointers, references, garbage collection, finalizers, pass-by-reference vs. pass-by-value, virtual C++ destructors, or the differences between C# structs and classes. They also know nothing about process. Waterfall? Spiral? Agile? Forget it. They've never seen a requirements document, they've never written a design document, they've never drawn a UML diagram, and they haven't even heard of a sequence diagram.

Michael Braude, "Why I'll never be a web guy" (vía Coding Horror)

Do You Believe In Magic?

If you want to see my prediction about superstitious programming decisions come true, find a place where there is no logical reason not to use eval() in your code, put eval() in there, and watch motherfuckers freak out like you were throwing elephant dung at the Pope. There are plenty of places where it is intensely dangerous and foolish to use eval(). I'm not disputing that. But it is so easy to find ways to use eval() which are completely immune to any danger of any kind, and yet which still trigger paranoia in superstitious programmers.

"Do You Believe In Magic?", Giles Bowkett

It's not the framework, it's you

Ruby on Rails has something that transcends the framework itself. It has you. The Mighty Developer. The Early Adopter. The Status Quo Challenger. The so-called Community —whatever that means.

"It's not the framework, it's you", Javier Ramírez

EuRuKo '2009

Este fin de semana la comunidad Ruby tiene una nueva excusa para irse de juerga cita para intercambiar conocimientos y experiencias: EuRuKo (Conferencia Europea de Ruby) '2009, esta vez en Barcelona. Las mejores charlas, la mejor gente, y a disfrutar

Si vas a estar allí, nos vemos (si no nos conocemos dame un toquecillo si me ves), y si no, pues ya sabes, lo de siempre: twitter (bot incluído), flickr, ajo, agua, y envidia cochina

¿El emperador está en pelotas?

Sinceramente espero que no, pero a veces la fe me flaquea ;-)

Os contaré una pequeña historia, y saldré un poco del armario en el proceso: antes de trabajar en serio con Rails y Ruby, nunca jamás había desarrollado con tests, y ni siquiera tenía muy claro cuáles podían ser las ventajas de hacerlo, ni cómo funcionaba el asunto. Fue la exposición a la (nunca suficientemente alabada) comunidad rubista y railera la que me hizo llegar a donde estoy hoy y llevo ya algún tiempo: a pensar que el testing es La Manera Correcta De Hacer Las Cosas™ (con toda la cautela e ironía que siempre lleva esa frase saliendo de mi boca). Quiero decir que todo lo que sé y opino acerca de testing, TDD, BDD, etc., es lo que he aprendido en estos casi cuatro años; no es poco, pero tampoco puede decirse que sea una visión muy general ni universal. Vaya eso por delante.

Durante este tiempo, mi arsenal de herramientas y técnicas ha ido creciendo y cambiando: Test::Unit, Shoulda, mocking, RSpec, integración contínua, tests de integración y aceptación, Cucumber, Selenium. El patrón de adopción era siempre similar: algo que te hacía perder un poco de productividad y velocidad de desarrollo, a cambio de mejorar la calidad de tu código, su mantenibilidad y en general su capacidad para no darte problemas en el futuro. De alguna forma, era el proceso contrario a la contracción de deuda técnica: comprabas productividad a medio y largo plazo pagándola con productividad a corto plazo. Suponiendo que el mundo no se acabe mañana, parece una buena idea, funcionaba y todo el mundo era feliz. Expresabas tus fuertes opiniones a los cuatro vientos, eras respetado por tu compromiso con el código de calidad, te ofrecían trabajar en proyectos la hostia de chulos, dabas conferencias, y ponías la palabra TESTING así en mayúsculas y a todo lo que da en pantallas de 4 metros.

Mientras tanto, en otro rincón de la galaxia (que sin embargo era básicamente el mismo), un grupo de entusiastas rubistas de Madrid andaba montando periódicamente Monsters of Ruby (también conocido en una de sus encarnaciones como Rails Hackathon). En el más reciente (semana pasada), la idea era poner a varios equipos a contruir en paralelo la misma aplicación, en Rails, al objeto de compartir conocimiento, contrastar enfoques, y ya de paso echarnos unas risas compitiendo. Dado que la mayoría de los participantes están acostumbrados a usar tests, se nos ocurrió que una buena manera de especificar a los equipos cuál era su cometido era entregar un conjunto de tests que la aplicación tenía que acabar pasando. Aceptación en estado puro. Con el entusiasmo del que cree en lo que hace, montamos un conjunto de pruebas para entregar a los equipos, con lo más chipén, state-of-the-art, lo que hay que hacer ahora para impresionar a las nenas y en definitiva lo que podéis escuchar en cualquier conversación en cualquier bar del país: RSpec, Cucumber, Webrat y Selenium. Como dice Dani, lo único que importa es molar.

EPIC FAIL

Ni uno sólo de los equipos completó, digamos, el 25% de la aplicación. ¿En qué momento de la historia que acabo de contar se torció todo? Parecía buena idea, éramos jóvenes y entusiastas y volveríamos a hacerlo. Todos y cada uno de los cambios de herramientas de los que he hablado fueron meditados, valorados en términos de trade-off y aceptados porque compensaban. El problema es que todos esos cambios eran paulatinos y apenas perceptibles en términos de productividad. Y tres años después, allí estábamos 3 desarrolladores competentes 3, necesitando una tarde entera para fabricar un formulario, un dolor de cabeza, unas ganas de llorar y un apetitoso perolo de sopa de rana. Ancas de batracio deconstruídas en salsa de desarrollador.

¿La conclusión? Parece ser que hemos estado viviendo una alucinación colectiva, nos hemos engañado los unos a los otros, nos hemos metido en una espiral de pérdida de productividad tan gradual como imparable, igual que la pobre rana que acaba cociéndose sin siquiera darse cuenta. El emperador está desnudo, y ha bastado un proyecto suficientemente simple e inocente (como un niño) para gritarlo a los cuatro vientos. No tiene sentido testear exhaustivamente una aplicación, sólo sirve para perder el tiempo.

Que levanten la mano los inocentones que piensen que el post acaba aquí =;-)

Esa conclusión puede ser una verdad clara y confortable, como las que necesitas en momentos de desasosiego, pero tiene mucho de equivocada. Ha habido en mi discurso unos cuantos argumentos algo mentirosos:

  • He hablado mucho de productividad, y con razón: es lo que debe guiar en todo momento nuestras decisiones. Lo que construímos en relación a los recursos (principalmente el tiempo) que empleamos para ello es lo único que da valor a nuestro trabajo. Si escribimos el código más elegante del mundo pero no somos productivos, no somos buenos programadores. El problema es cómo medirlo, y no voy a explayarme mucho porque hay ríos de tinta sobre ello. Un resumen rápido: simplemente, no se puede
  • También he hablado de trade-off entre productividad a corto plazo y productividad a medio/largo plazo. Aún en el caso de que pudiéramos medirla, ¿cuál es el tipo de interés de la productividad? No parece muy razonable invertir 1 hora hoy a cambio de 1 hora dentro de un mes. Pero, ¿y de 2, y de 3? ¿Dónde está el límite, cuánto tengo que obtener, para convertir una pérdida de tiempo en una buena idea? Tampoco lo sabemos
  • Resulta que en el proyecto que nos ocupaba no existía el largo plazo. Era un juego que duraba una tarde. En ese contexto (en el que el mundo se acaba mañana), no tiene sentido invertir
  • Hay otros factores que influyen en la rentabilidad de esa inversión: el tamaño y la complejidad del proyecto. Son variables también difíciles de medir, pero cuanto mayor y más complejo, más rentable será cualquier inversión en mantenibilidad (que es lo que hacemos al testear). En proyectos pequeños y simples, es menos interesante invertir
  • La relación entre tamaño y complejidad (aun cuando no es fácil medirlos) no es lineal. Un proyecto el doble de grande que otro (signifique eso lo que signifique), es más que el doble de complejo. Esto multiplica el efecto del punto anterior

Teniendo esto en cuenta, la conclusión no es tan clara y confortable, y además no es nada novedosa, y es que no hay herramientas ni técnicas correctas, sino herramientas y técnicas apropiadas para una tarea concreta. ¿Necesitábamos alforjas para este viaje? Probablemente no, pero puede servirnos para recalcar más aún la importancia de evaluar cómo vamos a enfocar cada proyecto, y no dejarnos llevar por la inercia. Aplicar un enfoque de testing tan exhaustivo a un proyecto simple y tan limitado en el tiempo pudo ser un EPIC FAIL (lo fue), pero eso no significa que en otro proyecto lo sea. Y de hecho, creo que en algunos (por ejemplo el nuestro actual) el EPIC FAIL sería el enfoque contrario.

El emperador está bien vestido y abrigado, lo que pasa es que el sábado fue a la playa y pasó un calor de cojones el pobre ;-)