Número de lecturas: 1008

Últimamente me he topado en varios sitios con el concepto “Defensive programming” o programación a la defensiva para los hispanohablantes y como parece que es un concepto que se está poniendo poco a poco de moda he decidido preparar un artículo hablando acerca de él. Espero que os resulte interesante y que os proporcione otra visión sobre la forma en que escribís vuestro código. Es probable que muchos de los conceptos de los que aquí hablo ya os resulten familiares; la “programación defensiva” se basa en ellos para conseguir que el código sea resiliente a los fallos que se produzcan en el futuro.

Así que sin entretenernos más vamos a adentrarnos en este concepto tan interesante de la “defensive programming”.


Programación defensiva. El concepto

La programación defensiva podemos entenderla como una forma de programar en la que el desarrollador anticipa los problemas que pueden surgir en el código. Gracias a esta previsión de la que nos dota esta metodología es posible prevenir futuros fallos y, lo que es más importante, conseguir que si finalmente acaban apareciendo, el programa no falle estrepitosamente.

Es decir, la programación defensiva no se basa en la utopía de escribir código que nunca falle, sino en la premisa de que cuando nuestras aplicaciones fallen (recuerda, nadie es infalible) el error se produzca dentro de un entorno lo más controlado posible. Si la programación defensiva fuera un dicho popular estaríamos hablando de

Caer con estilo

Para lograrlo, el código que escribamos deberá:

  • Fallar de forma segura, es decir tras el fallo no deberían quedar ficheros abiertos o bloqueados o transacciones contra la base de datos a medio terminar.
  • Fallar de forma clara, proporcionando siempre un código de error y un mensaje que explique lo que originó el fallo de modo que detectar el fallo y solucionarlo sea mucho más sencillo. Además, proporcionar una pila de llamadas e invocaciones siempre es una gran idea.

Por qué necesitamos la “programación defensiva”

Como comentaba líneas antes, cuando escribimos código nadie es infalible y, de hecho, por mucho cuidado que pongamos siempre habrá casos extremos (los conocidos casos frontera) que no hayamos tenido en cuenta y que provocarán que nuestra aplicación falle en el futuro.

Por tanto, la programación defensiva tiene algo de pragmatismo, es decir, de ayudar a tu yo del futuro a enfrentarse a problemas que hoy no estás viendo.

Este concepto de pragmatismo es desarrollado en el libro Pragmatic Programmer de Andrew Hunt y viene a proponernos que siempre:

  • Protejamos nuestro código de nuestros propios fallos y de todos aquellos que otros desarrolladores puedan provocar.
  • Validemos con tests el funcionamiento de nuestras aplicaciones.
  • Y puesto que es imposible contemplar en los tests todos los casos posibles que pueden suceder, controlemos el flujo de las excepciones y los códigos de error de cara a lograr esa “caída con estilo” de la que os hablaba antes.

Realmente esta idea de “programación defensiva” puede sonarnos a obsesión pero con los años aprendes a que no hay mejor forma de irse a dormir tras subir a producción que saber que el código está protegido contra cualquier fallo que suceda.

Photo by Ben Hershey on Unsplash

Algunas pautas para seguir la programación defensiva

Ahora que ya tenemos claro lo que es la programación defensiva quiero dejaros unas pautas que os pueden ayudar a sentiros más seguros con el código que escribáis y a seguir esta idea de “defensive programming”.

Una de las metodologías que podéis comenzar a emplear desde ya en vuestros desarrollos es TDD.

Su idea subyace en escribir siempre primero los casos de prueba unitarios para a continuación desarrollar la funcionalidad y ver si cumple las pruebas planteadas.

Por tanto, siempre tendremos una base sólida de pruebas automáticas que nos permitirán asegurarnos de que “nada se ha roto” a medida que el tamaño de nuestra aplicación crezca o refactoricemos la funcionalidad de los componentes. Si queréis, podéis pensar TDD como una metodología en la que cada componente crece en dos direcciones: funcionalidad y casos de prueba, de modo que los segundos aseguren el correcto funcionamiento del componente.

Además seguir una buena metodología TDD también nos ayudará a conseguir diseños poco acoplados pues nos obligaremos a aislar cada una de las funcionalidades cuando escribamos los casos de prueba. Esto nos permitirá tener un control mucho más detallado de cada funcionalidad y, lo que es más importante, asegurarnos de que obtenemos los resultados que esperábamos en cada llamada.

Independientemente de que sigáis la metodología TDD, es importante que revisemos siempre los casos frontera de la funcionalidad que estéis escribiendo.

Por casos frontera entendemos aquellos casos que no están dentro de la funcionalidad normal del componente sino a los que puede llegarse en determinadas ocasiones.

Por ejemplo si una función espera recibir un número natural, ¿qué sucede si le enviamos un número negativo? O si estamos trabajando con campos de texto, ¿se guardan correctamente los emojis en base de datos para us posterior visualización?

Son este tipo de condiciones o casos a las que deberemos prestar más atención cuando realicemos las pruebas ya que es aquí donde suceden la mayoría de los fallos. Por tanto, no os limitéis a preparar pruebas con condiciones esperadas (algo en lo que es muy fácil caer si no somos lo suficientemente críticos con nuestro código) sino pensad en los casos rebuscados e incluso “malintencionados” pues es ahí donde podremos detectar los puntos débiles de nuestra aplicación.

Otra forma de plantearte la programación defensiva es ponerte siempre en el peor caso. De este modo siempre estarás prevenido o alerta sobre casos que a simple vista no pueden parecer tan evidentes.

Por ejemplo, algo que me sucedió recientemente por haberme “confiado”. En el procesamiento de una llamada POST de una API todo funcionaba de forma perfecto (y pasaba todos los tests que se me habían ocurrido para esa funcionalidad) pero sin embargo, el caso más absurdo de todos provocaba fallo: un cuerpo de petición vacío.

Es por eso por lo que la programación defensiva nos insta a ir siempre con todos nuestros sentidos activos de modo que podamos anticiparnos a los futuros fallos de nuestra aplicación. Realmente, más que una metodología de trabajo como puede ser el ya mencionada TDD es una actitud ante la forma en que escribimos nuestro código con el objetivo de añadirle un extra de seguridad; es por eso que me ha resultado tan interesante: basta con tan sólo cambiar el chip para aplicarla, no se necesita ningún conocimiento especial.

Otra de las forma en las que podemos asumir esta idea de la programación defensiva es obligarnos a escribir código limpio y optimizado, de modo que su mantenimiento y la corrección de fallos sea mucho más sencillo.

Algunos consejos que nos pueden ayudar a escribir código limpio son:

  • Emplear patrones de diseño e intentar adoptar los principios SOLID en nuestros diseños. Los primeros nos permitirán resolver situaciones de una forma estándar (y optimizada) con la que la mayoría de desarrolladores ya estarán familiarizados mientras que los segundo nos ayudarán a obtener aplicaciones con bajo acoplamiento y mucho más fáciles de extender y mantener.
  • Familiarízate con el lenguaje de programación que estés empleando, no te quedes sólo en la superficie. Tómate tu tiempo en leer código de otros desarrolladores, artículos y publicaciones donde se emplee tu lenguaje favorito y descubre nuevas formas de hacer las cosas y todas las herramientas de las que dispones. Tanto Javascript como PHP están constantemente añadiendo nueva funcionalidad y recursos sintácticos para simplificar el código, por lo que no te quedes atrás.
  • Nombra bien los elementos. No hay nada como seguir una buena metodología de nombres para variables, funciones y clases a la hora de mantener el código. Si una función envía un email nómbrala apropiadamente y no recurras a nombres cortos para los elementos pensando que son más elegantes. A la larga agradecerás haber premiado que sean más descriptivos que cortos.
  • No confíes en nada ni des nada por supuesto. Muchos de los errores que suceden en el futuro se deben a dar por supuestos elementos que dependen de terceros por lo que no asumas que siempre serán correctos. Mejor prepara tu código ante posibles casos en los que no recibas lo que esperabas. Esto suele ser muy habitual en el tratamiento de datos introducidos por usuarios o en ficheros de inicialización, donde cometer fallos suele ser bastante habitual.
  • ¡DRY! Es decir, Don’t repeat your self. Evita duplicar funcionalidad por distintas partes del programa. Aíslala y agrúpala en un componente que puedas probar y que te permita realizar modificaciones en un único archivo sin tener que realizar el mismo cambio múltiples veces. Además, familiarízate con los sistemas de caché que tienes a tu disposición de cara a evitar calcular el mismo dato una y otra vez penalizando el rendimiento de la aplicación.
  • Loggea los errores. Otro de los elementos que nos ayudarán a resolver fallos futuros es loggear aquellas partes de nuestro programa que estén más propensas a fallar, de modo que, cuando finalmente se produzca un error podamos recorrer la traza de lo que sucedió y resolverlo más rápidamente.

Photo by NeONBRAND on Unsplash

Conclusiones

Como ya comentaba en un párrafo anterior, todo lo que he leído acerca de la programación defensiva me ha llevado a la conclusión de que se trata de una metodología destinada a ayudarnos a anticipar los fallos, no sólo los del presente (es decir, aquellos que encontramos a medida que desarrollamos el código) sino en el futuro de modo que, cuando se produzcan, nuestra aplicación este preparada adecuadamente para responder a ellos.

¿Verdad que no hay nada más desagradable que encontrarnos con un 500 en una aplicación web con el que no contábamos en ningún momento? Es aquí donde la programación adquiere todo su sentido y por lo que conviene tenerla en mente mientras desarrollemos. Los beneficios a la larga son realmente grandes.

Fuente:

View story at Medium.com

Be Sociable, Share!