El arte de la programación

Destacado

Considero que la programación tiene tanto de ciencia como de arte, desarrollar código, como cualquier actividad que implique la realización personal de algo, además de gratificante requiere del uso de la creatividad. Parto de la premisa de que un programa no solo tienen que ser funcional, tiene que ser legible, elegante, eficiente y lo más simple posible (pero no más).

Uso (o he usado) diferentes lenguajes de programación C, C++, Java, PHP, HTML, Python, RPG, en los que encuentro virtudes en cada uno de ellos, pero tengo que reconocer mi debilidad por el C, en el que comencé a finales de los 80. El lenguaje C te da la libertad de hacer las cosas a tu manera (libertad que incluye hacer las cosas mal, pero libertad al fin y al cabo). 

Cuando desarrollas es necesario ser consciente también de las dificultades del lenguaje que has elegido. En general al lenguaje C se le achacan fundamentalmente:

  • El hecho de dejar libertad al programador hace el lenguaje más propenso a errores. Como el lenguaje C es usado para programar sistemas o elementos críticos los errores tienen también mayor calado
  • La curva de aprendizaje del lenguaje es más amplia
  • Codificar en C, un lenguaje más cercano al bajo nivel, requiere más tiempo para realizar la aplicación que los lenguajes interpretados. (Aquí hay que hacer uso de buenas librerías y no pretender reinventar la rueda en cada desarrollo, ver las posibilidades de la generación de código y reutilización de rutinas)

Cuando consideras que la programación es algo más que codificar instrucciones y pretendes hacerlo de la mejor manera posible es conveniente seguir ciertas pautas, cierta filosofía de programación. La que voy a mostrar aquí es la que Eric S. Raymond recopiló basada en las filosofías de programación de los pioneros en el desarrollo de UNIX en su libro 'The Art of UNIX Programming', y que son aplicables a la programación en general en cualquier entorno. (No es una traducción literal de las reglas):

Regla de modularidad: Escribe partes simples conectadas por interfaces limpias

Controlar la complejidad es la esencia de la programación de computadoras.- Brian Kernighan

Depurar un programa domina el tiempo de desarrollo, y lograr que un sistema funcione no es tanto el resultado de un diseño brillante como el de no tropezarse demasiado muchas veces. La única forma de escribir un software complejo que no te explote en la cara es mantener baja su complejidad global: construirla a partir de partes simples conectadas por interfaces bien definidas, de modo que la mayoría de los problemas sean locales y pueda tener alguna esperanza de actualizar una parte sin romper el todo.

Regla de claridad: La claridad es mejor que el ingenio

Escribir programas teniendo en cuenta la gente que leerá el código. No solo comentar el código, es necesario programar de forma clara evitando rutinas complejas, más difíciles de leer por futuros mantenedores (entre los que estamos nosotros mismos)

Regla de composición: Diseñe programas para conectarse a otros programas

En el estilo de programación que se sigue en los sistemas UNIX se recomienda escribir programas que lean y escriban formatos simples, textuales, orientados a flujos e independientes del dispositivo. En Unix clásico, la mayor cantidad posible de programas se escriben como filtros simples, que toman una secuencia de texto simple en la entrada y la procesan en otra secuencia de texto simple en la salida.

Regla de separación: Separar la política de los mecanismos; interfaces separadas de los motores

Se debe separar las interfaces de los motores de tal manera que la modificación de unos no afecte a los otros. Esto se puede realizar de diferentes formas, bien como librerías que son llamadas por otro código o como un sistema front-end - back-end. Esta forma de proceder nos permite experimentar con nuevas políticas sin romper los mecanismos y también es mucho más sencillo escribir buenos test para probar los mecanismos.

Regla de simplicidad: Diseñar para la simplicidad; agregue complejidad solo donde sea necesario

Todo debe hacerse lo más simple posible, pero no más.- Albert Einstein

La excesiva complejidad de una aplicación puede venir por la presión de añadir características diferenciadoras a una aplicación, o por las ganas de destacar de algún desarrollador, pero al final el resultado es un código engordado difícil de manejar y con errores difíciles de detectar. La única forma de evitar estas trampas es alentar una cultura de software que sepa que lo pequeño es bello, que resiste activamente la hinchazón y la complejidad: una tradición de ingeniería que valora soluciones simples, que busca la manera de dividir los sistemas de programas en pequeños piezas colaboradoras.

Regla de parsimonia: Escriba un programa grande solo cuando quede claro por demostración que nada más servirá

"Grande" aquí tiene el sentido tanto de gran volumen de código como de complejidad interna. Esto permite a los programas que se hagan muy costosos en su mantenimiento. Los grandes programas invitan a la inversión excesiva en enfoques fallidos que no son óptimos, debido a que las personas son reacias a deshacerse de un producto grande que ha costado mucho trabajo.

La complejidad es costosa: El software complejo es más difícil de elaborar, más difícil de probar, más difícil de depurar y más difícil de mantener y sobre todo, más difícil de aprender y usar. Los costos de la complejidad, por difíciles que sean durante el desarrollo, son más difíciles después del despliegue. La complejidad crea lugares para que aniden los errores, de los que surgirán para causar problemas al mundo durante toda la vida útil de su software.

Las tres fuentes de la complejidad son:

  • Los programadores tienden a centrarse en la complejidad de la implementación, básicamente, el grado de dificultad que experimentará un programador al intentar comprender un programa para que pueda modelarlo o depurarlo mentalmente.
  • Clientes y usuarios, por otro lado, tienden a ver complejidad en términos de la complejidad de la interfaz del programa.
  • Impulsado por ambas hay una tercera medida que es mucho más simple: la cantidad total de líneas de código en el sistema, su tamaño de base de código. En términos de costos del ciclo de vida, esta suele ser la medida más importante.

Regla de transparencia: Diseñe para la visibilidad con el fin de facilitar la inspección y el depurado del programa

Debido a que la depuración del código a menudo ocupa tres cuartas partes o más del tiempo de desarrollo, el trabajo realizado temprano para facilitar la depuración puede ser una muy buena inversión. Una forma particularmente efectiva para facilitar la depuración es diseñar para la transparencia y la visibilidad.

Un sistema de software es transparente cuando puede verlo y comprender de inmediato qué está haciendo y cómo. Se puede detectar este tipo de software porque tienen funciones para monitorizar y mostrar el estado interno para que su programa no solo funcione bien, sino que también se vea que funciona bien.

Regla de robustez: La robustez es hija de la transparencia y la simplicidad

Se dice que el software es robusto cuando funciona bien tanto como en condiciones normales como en condiciones inesperadas que lo estresan por encima de las presunciones del diseñador. 

La mayoría de los softwares son frágiles y fallan porque la mayoría de los programas son demasiado complicados para que un cerebro humano los comprenda a la vez. Cuando no puede razonar correctamente sobre las entrañas de un programa, no puede estar seguro de que sea correcto, y no puede arreglarlo si está roto.

Se deduce que la forma de hacer programas robustos es hacer que su código sea fácil de razonar para los seres humanos. Hay dos formas principales de hacerlo: transparencia y simplicidad.

Regla de representación: Incorpora el conocimiento en los datos para que la lógica del programa pueda ser sencilla y robusta

Incluso la lógica de procedimiento más simple es difícil de verificar para los humanos, pero las estructuras de datos bastante complejas son bastante fáciles de modelar y razonar.

Los datos son más manejables que la lógica del programa. Por tanto cuando tenga que elegir entre la complejidad en las estructuras de datos y la complejidad en el código, elija la primera. Al desarrollar un diseño, debe buscar activamente formas de cambiar la complejidad del código a los datos.

Regla de la menor sorpresa: En el diseño de la interfaz, siempre haz lo menos sorprendente

Los programas más fáciles de usar son aquellos que exigen el menor aprendizaje por parte del usuario o, para decirlo de otra manera, los programas más fáciles de usar son los que se conectan con mayor efectividad al conocimiento preexistente del usuario.

Por lo tanto, evite la novedad gratuita y el ingenio excesivo en el diseño de la interfaz. Si está escribiendo un programa de calculadora, '+' siempre debe significar sumar! Cuando diseñe una interfaz, crea las interfaces de programas funcionalmente similares o análogas con las que es probable que sus usuarios estén familiarizados.

Presta atención a tu audiencia esperada. Pueden ser usuarios finales, pueden ser otros programadores o administradores de sistemas.

La otra cara de la Regla de la menor sorpresa es evitar que las cosas sean superficialmente similares pero realmente un poco diferentes. Esto es extremadamente traicionero porque la aparente familiaridad genera falsas expectativas. A menudo es mejor hacer las cosas claramente diferentes que hacerlas casi iguales.

Regla del silencio: Cuando un programa no tiene nada sorprendente que decir, no debe decir nada

Una de las reglas de diseño más antiguas y persistentes de Unix es que cuando un programa no tiene nada interesante o sorprendente que decir, debe callarse. Los programas Unix que se comportan bien hacen su trabajo discretamente, con un mínimo de alboroto y molestia. El silencio es oro.

Aunque la regla proviene de tiempos anteriores a los monitores, en los que la salida de información era muy lenta, la regla sigue vigente actualmente.

Cuando la salida de su programa se convierte en la entrada de otra persona, debería ser fácil seleccionar los bits necesarios. Para las personas es un factor humano de necesidad: la información importante no debe mezclarse con la verborrea sobre el comportamiento del programa interno. Si toda la información que se muestra es importante, la información importante es fácil de encontrar.

Los programas bien diseñados tratan la atención y la concentración del usuario como un recurso valioso y limitado, solo para reclamar cuando sea necesario.- Ken Arnold

Regla de reparación: Repare lo que pueda, pero cuando deba fallar, falle ruidosamente y lo antes posible

El software debe ser transparente en la forma en que falla, así como también en el funcionamiento normal. Lo mejor es que el software pueda hacer frente a condiciones inesperadas adaptándose a ellas, pero los peores tipos de errores son aquellos en los que la reparación no tiene éxito y el problema causa una corrupción silenciosa que no aparece hasta mucho más tarde.

Por lo tanto, escriba su software para hacer frente a las entradas incorrectas y sus propios errores de ejecución tan elegantemente como sea posible. Pero cuando esto no se puede, haga que falle de una manera que haga que el diagnóstico del problema sea lo más fácil posible. 

Regla de la economía: El tiempo del programador es costoso; consérvelo antes que el tiempo de la máquina

Con máquinas cada vez más potentes, la necesidad de ahorrar memoria y optimizar cada línea de código al máximo no es la misma que hace 30 años.

Si tomamos esta máxima muy en serio a lo largo del desarrollo de software, la mayoría de las aplicaciones se escribirían en lenguajes de alto nivel como Perl, Tcl, Python, Java, Lisp e incluso shell, idiomas que alivian la carga del programador haciendo su propia gestión de memoria.

Esta regla también se tiene que tener en cuenta cuando desarrollamos en C o C++, pudiendo hacer una gestión de memoria más generosa cuando el tipo de aplicación lo permita (con menos malloc y free, y con funciones que inicien y reserven memoria), que permite liberar en cierta medida al programador de la gestión directa de memoria.

Regla de generación: Evite codificar a mano; escribe programas que escriban programas cuando sea posible

El código generado (en todos los niveles) es casi siempre más barato y más confiable que el codificado a mano. Los seres humanos cometemos errores y en consecuencia, cualquier tipo de codificación manual de programas es una fuente para los retrasos y errores.

Todos sabemos que esto es cierto (es por eso que tenemos compiladores e intérpretes, después de todo), pero a menudo no pensamos en las implicaciones. El código de lenguaje de alto nivel que es repetitivo y soporifero de escribir para los humanos es un objetivo tan productivo para un generador de código como el código máquina.

Vale la pena usar generadores de código cuando pueden elevar el nivel de abstracción, es decir, cuando el lenguaje de especificación para el generador es más simple que el código generado, y el código no tiene que ser codificado manualmente después. 

Regla de optimización: Haz un prototipo antes del pulido. Haz que funcione antes de optimizarlo

El 90% de la funcionalidad entregada ahora es mejor que el 100% entregado nunca.- Kernighan & Plauger

Precipitarse para optimizar antes de que se conozcan los cuellos de botella puede arruinar cualquier desarrollo. Desde códigos torturados hasta diseños de datos incomprensibles, los resultados de obsesionarse con la velocidad, la memoria o el uso del disco a expensas de la transparencia y la simplicidad están en todas partes. Generan innumerables errores y cuestan millones de horas-hombre, a menudo solo para obtener ganancias marginales en el uso de algún recurso mucho menos costoso que el tiempo de depuración.

A menudo, la optimización local prematura dificulta la optimización global (y por lo tanto, reduce el rendimiento general). Una parte optimizada de forma prematura de un diseño frecuentemente interfiere con cambios que tendrían beneficios mucho más altos en todo el diseño, por lo que se obtiene un rendimiento inferior y un código excesivamente complejo.

La creación de prototipos es importante tanto para el diseño del sistema como para la optimización: es mucho más fácil juzgar si un prototipo hace lo que se desea que leer una especificación larga.

Uno de mis días más productivos fue eliminar 1000 líneas de código.- Ken Thompson

Regla de la Diversidad: Desconfíe de todos los que claman por  "un solo camino verdadero"

Incluso las mejores herramientas de software tienden a estar limitadas por la imaginación de sus diseñadores. Nadie es lo suficientemente inteligente como para optimizar todo, ni para anticipar todos los usos en los que se puede aplicar su software. Un diseño rígido y cerrado que no se comunique con el resto del mundo es una forma poco saludable de arrogancia.

Por lo tanto, la tradición de Unix incluye una desconfianza saludable de los enfoques de "un solo camino verdadero" para el diseño o la implementación del software. Abarca múltiples idiomas, sistemas abiertos extensibles y ganchos de personalización en todas partes.

Regla de extensibilidad: Diseñe para el futuro, porque estará aquí antes de lo que cree

Si no es prudente confiar en los reclamos de otras personas por "un solo camino verdadero", es aún más iluso creer en ellos sobre sus propios diseños. Nunca suponga que tiene la respuesta final. Por lo tanto, deje espacio para que sus datos y su código crezcan; de lo contrario, encontrará con frecuencia que está encerrado en elecciones tempranas imprudentes porque no puede cambiarlas mientras mantiene la compatibilidad con versiones anteriores.

Cuando diseña protocolos o formatos de archivo hágalos suficientemente autodescriptivos para que sean extensibles. Siempre, siempre, incluya un número de versión o componga el formato a partir de cláusulas autocontenidas y autodescriptivas, de forma tal que las nuevas cláusulas puedan agregarse fácilmente y las antiguas eliminarse sin confundir el formato de lectura del código.

Cuando diseñe el código, organícelo para que los futuros desarrolladores puedan conectar nuevas funciones en la arquitectura sin tener que desechar y reconstruir la arquitectura. Esta regla no es una licencia para agregar funciones que aún no necesita; es un consejo de escribir tu código de tal forma que agregar funciones más adelante cuando lo necesite sea fácil. Haga las juntas flexibles, y ponga "Si alguna vez necesita ..." como comentario en su código. Le debes esta cortesía a las personas que usarán y mantendrán tu código después de ti.

Tú estarás allí en el futuro también, conservando el código que quizás has medio olvidado bajo la presión de proyectos más recientes. Cuando diseñas para el futuro, la cordura que guardas puede ser la tuya.

 

Modificado por última vez enSábado, 25 Abril 2020 22:01
(1 Voto)
Etiquetado como :

Más en esta categoría:

Manejo de strings en C »

Deja un comentario

Asegúrese de introducir toda la información requerida, indicada por un asterisco (*). No se permite código HTML.