viernes, 27 de febrero de 2009

La “respuesta” a “¿Cuál es el error?”, la ambigüedad, su resolución y, finalmente, la realidad.

question Estamos hablando del problema planteado en el post ¿Cuál es el error? Si no lo has leído (con sus comentarios) no vas a entender nada (es simple y cortito, no hay excusa).

Me encanta ese problema ya que es disparador de un montón de cuestiones relacionadas a la codificación entre las que encontraremos muchas zonas grises. Algunos (como yo) verán las suficientes para afirmar sin lugar a dudas que programar está a medio camino entre el arte y la técnica, y que para el ojo entrenado no es difícil encontrar cierta belleza (o por lo menos elegancia, una de sus formas) en algunos ejemplos.

Bueh, tal vez me emocioné un poco. De todas maneras lo anterior sirve para aclarar que lo que sigue es mi personalísima opinión y que si bien puede ser contraria a la de algunos de ustedes no pretende ni puede invalidarla en manera alguna ya que, como establecimos, son zonas grises.

La solución “estricta”.

Siendo riguroso con el enunciado del problema, creo que la respuesta más acertada es “No se puede saber”.

Parece que hay un error (y aunque eso parece claro, si leyeron los comentarios en la entrada del planteo verán que hasta eso puede objetarse) ya que la declaración dice que suma pero la implementación resta. No hay forma de decidir qué está mal sin un contexto.

Lo más que podemos decir es que el código es poco legible, que es confuso, ambigüo (para un ser humano) o traicionero. Lo que no podemos decir (sin contexto) con seguridad es que hay un error de esos que le interesan a la gente común, y mucho menos decir cuál es. El único error que podemos verificar es de estilo o prolijidad, y no podemos resolverlo.

De acuerdo al contexto el error podría ser nimio, de esos que uno corrije sin decir nada a nadie (sería obviamente de tipeo, el programador quiso poner “+” y le salió un “-”. Algunos de los muchos números del reporte –por ejemplo- salieron mal, pero eran tantos que era difícil darse cuenta. En las pruebas, para un analista que interprete esos números, será mucho más evidente que para un programador cansado o aburrido de hacer reportes)… o grosero, muy grosero. Que se entienda bien clarito: al programador que utilice eso para restar, repetida y consistentemente en cualquier porción de código, yo le deseo la muerte.

Mejor volvamos a nuestro problema, sin contexto. Uno de los principios de diseño de Guido Van Rossum (autor de Python, entre otras cosas) nos dice:

Cuando te enfrentes a la ambigüedad, rechaza la tentación de adivinar.

Es muy, muy difícil de seguir. Casi tanto como decir “No sé” o “No se puede saber”. La primera respuesta es dolorosa para un orgullo cultivado (y los programadores sí que cultivamos el nuestro) y la segunda requiere mucha confianza y conocimiento del tema que se trata.

Por otro lado las preguntas engañan. No es lo mismo preguntar “¿Cuál es el problema?” que “¿Hay un problema?”.

Vivimos en un mundo imperfecto y una computadora no entiende de imperfecciones. A los programadores nos toca ser el último eslabón entre lo ambigüo por naturaleza (el lenguaje, los negocios, el ser humano, la vida en general) y lo absoluto (una sucesión finita de operaciones), así que lidiamos con este problema -de enfrentarnos a la ambigüedad- todos los días.

Un ego desmedido como el nuestro no digiere fácilmente que (si bien podemos hacer cualquier cosa) no somos dueños de la verdad absoluta (no todo lo que hagamos estará bien -¡aunque funcione!-) y que -peor todavía- existe la verdad absoluta y tiene dueño: el cliente. Utilizando estrictamente las palabras, los programadores somos dueños de la implementación, pero no de la solución. Decidimos cómo pero no qué.

El cliente (o quien lo represente ante nosotros, los programadores) puede equivocarse en muchas cosas, pero cuando hay que resolver una ambigüedad tiene no sólo la última, sino la única palabra. En el trabajo de todos los días no podemos decidir si “una operación revertida aparece en el informe o no”. ¿Por qué no? Porque estaríamos inventando y tal vez invirtiendo tiempo en hacer bien algo que nunca debería haberse hecho.

Recuerden siempre: las preguntas engañan. Siguiendo el ejemplo de la operación revertida, es probable que hayamos arribado a esa opción binaria más por nuestra naturaleza de programadores que por la realidad de la situación. ¿El cliente pensó en esta situación? No. Entonces, ¿cómo podemos saber que la solución es una u otra, y no una tercera, o ninguna, o las dos?

¿Cómo saber si hay que sumar o restar? No lo sabemos. No sabemos qué es lo que hay que hacer si no nos lo comunican, tenemos que preguntar. La frase de Guido nos está diciendo “más fácil inventar parece, pero evita la tentación, porque buscar una respuesta debes” (¿o ése era Yoda?). Ésa es, en principio, nuestra responsabilidad: resolver la ambigüedad preguntando, no inventando.

Es también, entonces, un tema de responsabilidades (“Un gran poder conlleva una gran responsabilidad”): ¿tenemos ese poder para decidir? ¿queremos esa responsabilidad? Recordemos, otra vez, que estaríamos tomando una decisión sin conocer todos los detalles: no conocemos el negocio, al cliente final, ni la situación comercial… tal vez ni siquiera el sistema completo.

Pero, ya lo dijimos, vivimos en un mundo imperfecto. Muchas veces (en todo proyecto real esta situación es más que frecuente) el cliente no está disponible (si estuviese disponible él tiene la respuesta, aunque ésta sea “hacé lo que te parezca” o tire una moneda o cambie con el tiempo) o no entiende la pregunta. ¿Entonces?

La frase nos dice “evita la tentación”, no nos dice “no adivines”. Hay veces que tenemos que adivinar. Presionados por un sinfín de circunstancias, para bien o para mal, hay veces que hay que adivinar y seguir adelante, rellenar los huecos en las especificaciones apelando al sentido común o, en última instancia (es lo que yo lo recomiendo) hacer lo que sea más fácil para nosotros, así por lo menos el tiempo perdido será menor.

Pero tenemos que ser conscientes de que lo estamos haciendo. Y de que está bien si lo hacemos sólo si hemos agotado otras instancias (o no tenemos tiempo de hacerlo) y no nos queda otra, y no para patear un problema debajo de la alfombra.

La razón última por la que un proyecto no puede llevarse a cabo sin la cooperación y compromiso real de los programadores que en él participan (por lo menos de buena parte de ellos) es que (como una computadora no entiende de ambigüedades) estas pequeñas decisiones…

  • que nos vemos obligados a tomar sin información completa
  • o con la poca que logremos recabar investigando hasta donde dé nuestra conciencia,
  • basándonos en nuestra experiencia, sentido común, pálpito y buena suerte,
  • sabiendo que en el mejor de los casos evitaremos un problema y nadie se dará cuenta ni agradecerá nada,
  • y que en el peor de los casos… (mejor no hablar de ello)

…tienen gran impacto (para bien o para mal) en el resultado. Tanto que son determinantes, tal vez no del éxito (que dependerá también de muchos otros factores), pero seguro que del fracaso de un proyecto (que con una buena cantidad de decisiones equivocadas o con apenas un par en los lugares precisos puede darse por muerto).

La mejor manera de sabotear un proyecto es hacer exactamente lo que te piden, sin chistar.

8 comentarios:

Cerebrado dijo...

Finalmente iba a pasar. Te volviste mas estupido nomas.

Tu post se llama "¿Cuál es el error"?, no "¿Existe un error?".
Si preguntás cuál es el error, la gente tiende a ver un error.

Podría incluso no ser un error según la especificación.

No me hagas perder mas el tiempo de ésta forma, porque sé donde vivís.

AcP dijo...

@Cerebrado: ¡Qué liiiindoooo! Yo también te quiero.

Yoriento dijo...

"La mejor manera de sabotear un proyecto es hacer exactamente lo que te piden, sin chistar."

Y se puede aplicar a cualquier ámbito donde se desarrollen proyectos. Qué gran frase. :-)

vgaltes dijo...

Esto con un buen testeo unitario no hubiera pasado! :P

fav dijo...

public void testSuma() {
decimal suma = Suma(A, B);
assert(suma, A - B);
}

All green.
Sí, si pasa. =P

vgaltes dijo...

Si que pasa, pero queda bien claro en el test que es lo que hace la función, que es una resta.

AcP dijo...

@fav: y si la querés complicar un poco, te diría que el test tampoco evitaría que alguien utilice la función "Suma" para sumar (¡qué idea loca, a quién se le ocurre!) a pesar de que su autor la haya utilizado para restar.

Claro que luego la historia seguiría y terminaría con final feliz: el segundo programador ve que su test falla. Cambia la implementación de la función "Suma" para que sume y se da cuenta (no puede evitarlo) que ahora su test funciona pero el test de "Suma" falla. Así que corrige el test de "Suma" y ahora fallan los test de las funcionalidades que la implementaban como resta, así que también corrige esto hasta que "all green".

Los tests son fabulosos.

Aunque en realidad puede no ser tan feliz... después ese programador va, busca al primero y le estampa dos tiros en la cabeza.

David dijo...

Creo que este asunto no es tan complejo como intentas "pintarlo".

Una de las máximas de java es que el nombre de los métodos debe ser descriptivo respecto al funcionamiento del mismo. Por lo tanto, exista o no documentación a cerca del método, este debería de realizar la suma de a y b.

En caso de que algún programador lo este usando para realizar una resta, ese tío debería aprender a programar. Ya que si yo quiero hacer una resta no uso un método que se llama Suma.

Si no existe un método resta(), lo ideal seria crearse uno, y no usar el método suma.

Un Saludo.