lunes, 28 de abril de 2003

YAGNI

Hace unos años, cuando cursaba cuarto año de la facultad, participe en un proyecto de desarrollo con unos compañeros.
En aquella época gran parte de mi atención estaba centrada en el diseño orientado a objetos. Probablemente Bertrand Meyer fue el responsable mayoritario de esto. Para ser sincero, en aquel tiempo pensaba que la orientación a objetos podía resolver gran parte de los problemas que aquejaban al desarrollo de software. Me parecía fantástico conceptualmente y me provocaba sorpresa que no toda la gente lo viera como yo.

En aquel entonces programaba en Borland Delphi y era muy entretenido ver en acción la herencia, el polimorfismo y usar la RTTI (run-time type library, similar al concepto de Reflection en Java). Dado este interés, en este proyecto decidimos hacer uso de objetos para modelar las entidades del negocio (clientes, proveedores, facturas).

Pero nos encontramos con el problema de la impedancia entre el modelo relacional y el orientado a objetos; básicamente el problema de traducir los objetos, sus atributos y sus relaciones en tablas y campos. En búsqueda de una manera optima de resolver este problema, leímos todo artículo relacionado con el tema que pudimos encontrar en Internet. Llegamos a la conclusión que debíamos construir una capa de persistencia, un conjunto de clases mediante el cual uno podría manipular objetos, guardarlos y consultarlos sin necesidad de conocer nada acerca de la base de datos. Algo similar a lo presentado por Scott Ambler en The Design of a Robust Persistence Layer For Relational Databases. Recuerdo que desarrollar una capa de persistencia era un problema que me resultaba muy motivante para resolver. Hay muchas dificultades para sortear, muchas decisiones para tomar, muchas características para agregar.

* ¿Cómo manejamos las colecciones dentro de un objeto?
* ¿Qué pasa cuando traemos a memoria 100 objetos desde la base de datos?
* ¿También traemos los objetos relacionados?
* ¿O sólo los construimos por demanda?
* ¿Cómo controlamos la concurrencia?
* ¿Cómo hacemos con los generadores de reportes y otros componentes que generalmente requieren un conjunto de datos con filas y columnas?
* ¿Cómo diseñamos la capa de persistencia para que sea independiente de RDBMS?
* ¿Permitimos uso de transacciones anidadas?

Si, definitivamente era divertido.

Pero estábamos en la universidad. Y las disquisiciones de valor académico que son válidas en ese entorno no lo son cuando salimos de él. El tiempo invertido en aquel proyecto de desarrollo para construir ese framework celestial para guardar objetos representaría un uso ineficiente de recursos para un proyecto real. No contábamos con elementos significativos de retroalimentación (¿qué le importa a un cliente si mapeo una jerarquía de herencia con una tabla o dos tablas?) e invertimos trabajo en funcionalidad que nunca se utilizó.

He observado que en un proyecto de desarrollo se presentan muchas oportunidades para invertir recursos en funcionalidad que no se necesita, aunque estas aparecen de manera sutil, como funcionalidad completamente razonable.

Por ejemplo, pensemos en una aplicación donde se emite un formulario impreso que puede tener dos formatos distintos según la categoría del cliente al que se le envía, "mayorista" o "minorista". Es algo simple de hacer, seria algo así:

if (Cliente = "mayorista") ImprimirFormulario(Formato1)
else ImprimirFormulario(Formato2)


No, mejor guardamos en una tabla con parámetros las categorías de cliente y los diseños de formulario que le corresponden. Al momento de imprimir consultamos la tabla de parámetros y aplicamos el diseño adecuado. Es más, podríamos guardar un template que represente el diseño del formulario en algún formato. Ya sé, ¡en XML! Y podríamos hacer un diseñador de templates donde el usuario podría hacer sus propios formularios y asignarlos a nuevas categorías de clientes si estas surgen en el futuro...

¡¡¡ Y A G N I !!!

Eso gritaría un practicante de Extreme Programming. YAGNI es un acrónimo que significa "You Aren't Gonna Need It" (No lo necesitarás). YAGNI representa la idea: siempre implemente algo cuando realmente lo necesite, nunca cuando supone que lo necesitará.

Ron Jeffries, reconocido desarrollador y pionero en el campo de las metodologías ágiles, lo explica claramente con un ejemplo:
Usted está trabajando en alguna clase. Sólo agrega la funcionalidad que necesita. Supone que va a necesitar algo de funcionalidad adicional. Si no la necesita ahora, no la agregue ahora.

Por qué no?

"OK, Sam, ¿por qué quieres agregarla ahora?"

"Bueno, Ron, nos ahorrará tiempo más adelante."

A menos que tu universo sea muy distinto al mío, no puedes ahorrar tiempo. Sólo puedes hacer menos. Entonces me estas diciendo:

"Vamos a poder hacer menos más adelante (a costo de hacer más ahora)."

A menos que tu proyecto sea muy distinto al mío, ya tienes mucho por hacer ahora. Hacer *más* ahora es algo muy malo cuando ya se tiene mucho por hacer. A menos que tu mente sea muy distinta a la mía, tienes una probabilidad distinta de cero de no necesitar esa funcionalidad después de todo, o de tener que retocarla aún antes de necesitarla cuando modifiques la clase por alguna razón.
Si algo de esto pasa, has desperdiciado tu tiempo completamente, además de asignarte más cosas para hacer ahora, cuando difícilmente necesitas más para hacer.

"Pero Ron, si lo hago ahora sabré como hacerlo, y más adelante tal vez no lo sepa."

"Entonces, Sam, me estás diciendo que la clase que estás escribiendo es tan compleja que ni tú serás capaz de mantenerla?"

Mantenla simple. Si necesitas la nueva funcionalidad, la puedes agregar más adelante. Si no la necesitas, no tendrás que hacer nada de ese trabajo. Tomate el día libre.

Por supuesto, no basta con decir YAGNI para que un elegante diseño evolutivo emerja espontáneamente. Hay dos técnicas fundamentales que hacen posible este diseño evolutivo: refactoring y TDD, temas lo suficientemente interesantes como para dedicarle un espacio en el futuro.

Cada vez que escribo, termino con más preguntas que respuestas. Esta vez no será la excepción.
¿Por qué nos cuesta tanto hacer sólo lo que realmente necesitamos?

No hay comentarios.: