Pensando en programar

Dentro de muy poco ya vamos a empezar a implementar -o por lo menos a explorar cómo implementar- nuestra solución, y tendremos que enfrentarnos a dificultades de variada naturaleza. Algunas serán más o menos simples detalles concretos derivados de las dificultades del problema en sí mismo. Otras, sin embargo, serán dificultades derivadas de la propia naturaleza de la programación que básicamente responden a la pregunta: Cuando haya varias formas de hacer las cosas, ¿cómo elegir la más apropiada?

Principios Fundamentales de la Programación

Una de las cosas que caracteriza a la programación es que, por lo general, existen muchas formas diferentes de hacer una cosa. Esto por un lado es una cualidad muy ventajosa, ya que nos da más posibilidades y oportunidades. Pero, por otro lado, supone una dificultad añadida ya que necesitamos saber cuál, de todas esas opciones, es la más apropiada.

A lo largo de los años, la comunidad de programadores ha ido desarrollando el conocimiento alrededor de todas estas opciones, tratando de proporcionar a quienes van llegando después a la programación toda una serie de guías, de normas, de buenas prácticas, que les ayuden a “hacer las cosas bien” -o por lo menos intentarlo-. Estas reglas, estas herramientas de guía, intentan recoger dentro de ellas ese conocimiento y aplicarlo de forma sencilla y práctica. Esta es una buena intención. Sin embargo, demasiadas veces termina ocurriendo que quienes las usan se limitan a seguirlas ciegamente, sin llegar a absorber ese conocimiento que llevan en su interior.

De modo que en lugar de limitarme a exponer una serie de buenas prácticas y de formas de hacer las cosas, personalmente creo que es más interesante intentar explicar directamente las razones y principios básicos de los que se derivan esas reglas o guías. En mi opinión, esto ayuda no solo a comprender mejor e interiorizar esas posibles prácticas sino que además nos da la capacidad de evaluarlas críticamente e incluso adaptarlas o derivar nuevas prácticas que se adapten mejor cuando encontremos situaciones nuevas que no habíamos contemplado antes.

Sé que es muy ambicioso -incluso pretencioso- por mi parte llamar a esto “principios fundamentales”. Digamos que esta es, por lo menos, una posible perspectiva. Seguramente no la única, pero creo sinceramente que los principios que siguen son una perspectiva válida y que funciona.

Economía del código

Economía. Suena aburrido… El dinero… Ah, pero ahí está la gracia. Cuando hable de “economía” en lo que sigue, debemos hacer un cierto esfuerzo por recordar que no estoy hablando de dinero. En algún caso a lo mejor sí, pero en la mayor parte no. Será difícil recordarlo porque hablaré de presupuestos, de coste, de valor, de beneficio. Y a pesar de todo eso seguiré sin estar hablando de dinero. Cuando hablo de economía del código, y en general de coste y de valor, hablo de algo más general que lo monetario.

Coste es cualquier recurso que debemos invertir o cualquier esfuerzo que debemos hacer. Es dinero, a veces, claro. Pero si, por ejemplo, estamos programando por diversión, también estamos invirtiendo tiempo, estamos aplicando nuestro esfuerzo, a lo mejor dejando de lado otras actividades que también podrían beneficiarnos, para hacer esto. En general, independientemente del motivo por el que escribamos un programa, estaremos invirtiendo muchas cosas, tiempo, dinero, esfuerzo…

El valor que tiene un programa también puede ser monetario y además de otros tipos. Puede que nos paguen por programarlo o que no, pero además el programa puede aportar otros varios valores o beneficios, a nosotros o a otras personas. Puede que nos proporcione satisfacción personal hacer un cierto desarrollo, o que nos enseñe algo. A veces ayudará a otras personas a realizar una tarea de su trabajo, o puede ayudarles a aprender cosas, o tener un contenido artístico y producir disfrute o hacer que su vida sea mejor en algún sentido. Puede que un cierto desarrollo produzca valores sociales o culturales. Todo esto son valores diferentes al puramente monetario y que también podemos tener en cuenta.

Existen, desde luego, otros valores más egoístas. Un programa puede usarse para obtener poder ilegítimamente, para manipular a otros, o para cometer actos delictivos o inmorales. Personalmente no comparto estos valores -e inocentemente tengo la esperanza de que quien lea esto tampoco los comparta-, pero es evidente que existe ese otro tipo de “valores”. En general, valor es cualquier beneficio que se obtenga, sea cual sea.

Buena solución

El medio por el que llegamos a la economía del código es respondiendo a la pregunta de ¿Qué es una buena solución? o ¿Cómo podemos evaluar lo buena -o menos- que es una solución? Y la respuesta es, en principio, sencilla.

Como punto de partida establecemos que el mínimo indispensable es que la solución resuelve el problema. Esto es evidente, y conviene no olvidarlo nunca, pero no es suficiente en sí mismo para decir que la solución sea buena. Una solución es buena o mejor, en la medida en que el beneficio total que obtenemos supera al coste total que requiere. Este podría ser uno de esos principios fundamentales de la programación que comentaba.

Un detalle interesante de este principio es que es aplicable en general, no solo en al conjunto total de la solución. Es decir, normalmente podemos aplicarlo directamente a cualquier decisión, grande o pequeña, que tengamos que tomar. Podemos aplicarlo a aspectos generales y globales, pero también a detalles concretos y específicos. Se trata simplemente de preguntarnos: Esto que voy a hacer, ¿tiene un balance total positivo o no?. Pero antes de quedarnos en esta pregunta y pensar que ya tenemos la clave para evaluar decisiones, veamos algunos detalles a tener en cuenta.

Ciclo de vida y contexto

Este es, con seguridad, uno de los aspectos más relevantes a la hora de analizar costes y beneficios y de evaluar la bondad de una cierta solución. El ciclo de vida del proyecto, de la solución y su contexto pueden cambiar radicalmente la evaluación de una decisión. Se trata de ser conscientes y tener presente el alcance -principalmente en tiempo- de la solución que queremos aplicar. ¿Cuánto tiempo necesita estar funcionando esta solución? ¿A cuántas cosas va a afectar?

Veamos algún ejemplo de cómo el ciclo de vida de un proyecto puede afectar a las decisiones que tomemos y prioridades que establezcamos.

Es un comportamiento habitual entre bastantes programadores defender como valor indiscutible características del código que favorecen su mantenibilidad. Cosas como escribir un código limpio, ordenado, con nombres expresivos, un código que sea modular, al que podamos añadir o cambiar cosas fácilmente más adelante. Esto en general es bueno, sí, pero imaginemos que nuestra necesidad, que lo que estemos programando sea algo que solo vamos a utilizar una única vez, para un proceso que debe hacerse en los próximos 3 días, y que luego, una vez realizado, no necesita más acción. En un caso así, todos los valores de mantenibilidad dejan de ser un beneficio real, pero todos los esfuerzos que hagamos para conseguirla, seguirán siendo costes. También podríamos imaginar que, por ejemplo, sacrificar cierta legibilidad puede darnos a veces un rendimiento extra. En una situación como la descrita puede ser una importante reducción de coste el terminar el proceso en dos días en lugar de tres.

Hace unos días, leía un comentario por ahí que se quejaba de que un prototipo hubiera sido escrito en determinado lenguaje. El argumento era algo así como que ahora, con ese lenguaje, iba a ser más difícil -i.e. costoso- mantenerlo porque, no siendo un lenguaje popular, había menos personas que lo conocieran bien. El argumento suena razonable y podríamos entonces pensar que había sido una mala decisión elegir ese lenguaje. Ahora bien, si tenemos en cuenta el contexto y el ciclo de vida del proyecto, el argumento debería ser irrelevante. Un prototipo es -o debería ser- un modelo que desarrollamos con el objetivo concreto y específico de evaluar ciertas características de una solución, como su viabilidad, su coste, aceptación por parte de los usuarios, etc. No es -o no debería ser- la base sobre la que luego desarrollemos el proyecto real. Su ciclo de vida queda agotado cuando termina esa evaluación inicial. 1)

Por supuesto, sería ideal encontrar decisiones y prácticas que podamos considerar universales, que sean las mejores para cualquier situación. En mi experiencia, la única práctica realmente universal es: Toma tus decisiones adaptándote a las circunstancias. Eso sí, hazlo teniendo como guía el criterio de conseguir un balance global positivo. Y ojo que la clave es que el balance no sea localmente positivo sino globalmente positivo. En ocasiones es muy razonable tener que hacer sacrificios que localmente/temporalmente tienen un balance negativo, pero que a largo plazo, o globalmente, terminan teniendo un balance positivo. 2)

Prácticas u objetivos generalmente interesantes

Podemos, eso sí, aventurar algunas características generalmente interesantes de las buenas soluciones. Es decir, no son necesariamente universales, pero sí son suficientemente comunes como para que sea buena idea favorecerlas o buscarlas.

Es bueno e interesante, por ejemplo, que nuestro código esté limpio y que sea razonablemente fácil de leer y entender. Esto no sólo lo hace más fácil de mantener, también lo hace más fácil de desarrollar inicialmente. Es también un buen valor a perseguir el hecho de que esté bien estructurado, que separemos las diferentes partes que resuelven diferentes aspectos del problema. Ya veremos más detalle de cómo hacer esto, pero en sí mismo parece claro que es algo que siempre ayuda positivamente al desarrollo.

Otra cosa “generalmente interesante” es el utilizar nombres adecuados para representar a las cosas. Esto será aplicable cuando estemos escribiendo código y tengamos que dar nombre a una variable o a una función o cualquier otra entidad concreta del código, pero también es interesante hacerlo cuando estamos haciendo otras cosas, como discutir el concepto con un compañero, presentarlo en un esquema o en una documentación, o simplemente cuando estemos pensando en él. El hecho de llamar a las cosas con nombres que las representen de manera suficientemente expresiva y correcta, siempre suele tener un balance global positivo y evita muchas confusiones.

Seguramente existen otros valores que, aunque no sean completamente universales, sí están cerca de serlo o sí son interesantes en la mayoría de casos. Pero debemos recordar que cuanto más específicos y detallados sean estos valores, lo normal es que su alcance y aplicabilidad también sean más restringidos. Por eso, por ahora, no me voy a extender más en buscarlos. Algunos quizá vayan surgiendo según avancemos.


Por ahora, quedémonos con esa idea básica. Nuestras soluciones y nuestras decisiones deberían estar guiadas siempre hacia un balance global positivo, hacia conseguir nuestro objetivo de la forma más eficaz y eficiente.


1)
Está claro que el problema, si acaso, es tratar de cambiar la definición del ciclo de vida después de haber creado la solución. Es una tentación comprensible la de intentar aprovechar el prototipo para más cosas, pero no podemos pretender que una decisión tomada en unas circunstancias concretas, siga siendo aplicable si decidimos cambiar las circunstancias.
2)
Algunos llaman a ese sacrificio “deuda técnica”, pero creo que el término está bastante corrupto y prefiero no utilizarlo demasiado.

Discusión

Escribe el comentario. Se permite la sintaxis wiki: