miércoles, 1 de diciembre de 2010

Sherlock Holmes

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

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

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

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

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

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

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

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

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

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

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

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

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

No hay comentarios:

Publicar un comentario