Haciendo lugar a la Excepción

 Las  plataformas modernas de desarrollo de software consideran el concepto de Excepción (Exception) como una pieza fundamental (hay que tener en cuenta que lenguajes más antiguos como C, Cobol o Pascal no las contemplaban). Comandos como try – catch hoy son moneda corriente tanto en Java como en la plataforma .NET
No obstante, quizás es mi impresión, nunca quedó del todo claro cual es la forma correcta de manejar las excepciones, y eso ha conllevado a malestares tanto al desarrollar el software como al mantenerlo. Y, en ocasiones, también al usarlo. Voy a resumir acá cuatro antipatterns. La lista es más extensa
  • "Excepcionalización" exagerada
    Supe del caso de un proyecto donde se tomó la decisión de aprovechar el concepto de excepción para abordar cualquier situación anómala. Todas las situaciones anómalas. Así, los módulos de ingreso de datos validaban la información recibida y si algún campo obligatorio estaba en blanco o un código no respetaba una dada máscara, eso arrojaba excepción. Simplemente porque la política era lanzar excepción ante toda condición anómala
    Por supuesto esto encareció el costo de codificación, ya que toda esa plomería de lanzado y captura de excepciones quedó como responsabilidad de los desarrolladores. Pero no fue todo. El código se volvió ilegible, ya que la gestión de estas excepciones estaba varias líneas de código más lejos. Ocasionalmente varios módulos en la pila de llamadas (callstack) más lejos
    Entonces podemos decir que se han provocado dos consecuencias no deseadas: demasiado código (too much code) e ilegibilidad
    Un enfoque más adecuado en este caso debería haber manejado las validaciones in situ, y ante datos mal cargados devolver el control señalando los errores en una forma sencilla y directa. Sin complicarse la vida
  • Intervención innecesaria
    Es normal tener bloques de código que incluyan llamadas a módulos que puedan arrojar excepción. Lo antinatural, acepto que no todos piensan como yo, es tener que prepararse siempre para reaccionar ante cada posible excepción. Recuerdo mi paso por el banco multinacional donde era imperativo encerrar entre try – catch todo aquel bloque de código de esta naturaleza, y estaba prohibido no hacer nada dentro del catch. Resultado: la dirección del proyecto terminó pidiendo a ciertos miembros del equipo de desarrollo que construyeran un robot que revisaba permanentemente el código del resto del equipo, buscando la presencia de catchs vacíos. Con el tiempo la dirección del proyecto terminó convenciéndose de que no siempre hay algo para decir ante la presencia de una excepción, pero sólo flexibilizaron su postura (y consiguientemente el robot) permitiendo que en esos casos se documentara con un comentario dentro del catch explicando por qué no se iba a intervenir. Hecha la ley hecha la trampa: a partir de ese día todos los desarrolladores comenzaron a poner comentarios del tipo /* dskjhdsakf fhdskahda dafhkjh */ y el arturito ése nunca más encontró excepciones mal manejadas
    Lo cierto es que en cada segmento de código habrá invocaciones a módulos que arrojaran excepciones que podremos manejar, donde podremos intervenir en consecuencia, y excepciones ante las cuales no tendrá sentido nuestra intervención. Ejemplo de esto último? El módulo de acceso a datos vuelve con SQLException quizás porque el string de conexión tiene expirada la contraseña. La llamada se hizo en la capa de negocio de la aplicación. Tiene sentido capturar allí esa excepción? Acaso tiene sentido establecer una nueva contraseña allí?
    Lo mejor es dejar que la excepción pase de largo hacia capas superiores. O bien, sí, acepto. Enmascararla en un tipo de excepción de más alto nivel pero sin perder la excepción original (la root cause que es la que tiene el stack trace genuino). Ver siguiente.
  • Inacción negligente
    Es el opuesto al caso previo. Es cuando, por el contrario, dejamos pasar siempre las excepciones que se produzcan y nunca intervenimos ni siquiera para enmascarar ciertos errores y así no estampar en la jeta del usuario el error del caso anterior donde la contraseña de la base de datos había expirado
    No intervenir nunca, como política, conduce a código de ejecución impredecible, pobremente documentado y esto atenta contra su fiabilidad por parte de los desarrolladores y su usabilidad por parte de los usuarios
    Cuál debiera ser la correcta intervención para no caer en el antipatrón anterior? Como contaba antes, enmascarar adecuadamente el error de bajo nivel en uno del nivel del módulo en que nos encontramos. Especial atención aquí, como decía previamente, de anidar la excepción original ya que la excepción nueva será para presentar en el nivel contextual en que nos encontramos, pero la original servirá, en última instancia, por saber que fue lo que lo provocó. En otras palabras, la excepción actual será la que se presentará al usuario de la aplicación. La original será la que se logueará para que el equipo de sistemas conozca lo que ocurrió realmente y pueda prevenirlo a futuro. Creo que todos alguna vez nos hemos topado con un sitio web donde al pulsar un botón de acción hemos recibido un bello mensaje en letras rojas, estilo [ODBC Exception, SQL code = …]. Mejor hubiera sido loguear ese error y presentar al usuario Momentáneamente no fue posible realizar su reserva
  • Tratamiento inoportuno
    Un cliente alguna vez me reclamo de la poca fiabilidad de .NET respecto del manejo de excepciones. Me decía que ellos como política, ante cierto rango de excepciones tenían un handler que invocaban en el catch, que a su vez generaba un registro con la excepcion a fin de tomar estadísticas mensuales de los fallos que el código producía. Su recriminación hacia .NET era que mientras el handler estaba generando el registro en la base, el usuario web quedaba esperando como un tonto una respuesta que encima iba a ser anómala
    La realidad es que la lógica de un catch se ejecuta by design en el mismo hilo que la lógica que provocó la excepción. Por consiguiente si estamos urgidos por anunciar al usuario que no se pudo, pero a la vez queremos generar el registro para estadísticas, lo aconsejable aquí es aplicar mecanismos de fire and forget en el handler de excepciones. Ejemplo? Postear un mensaje en una cola cuyo consumidor -necesariamente en otro hilo- completará la acción de registrarlo en la base de datos. Entonces el handler sólo postea a la cola y devuelve el control al catch que lo invocó que a su vez terminará y devolverá el control al cliente. La lógica de persistencia queda entonces desacoplada y se ejecutará en forma asíncrona

Invito a contar vuestras experiencias, antipatterns y sus consiguientes best practices en el manejo de las excepciones

Esta entrada fue publicada en patterns and practices. Guarda el enlace permanente.

2 respuestas a Haciendo lugar a la Excepción

  1. Patrick dijo:

    Diego,
     
    Después de leer tu post, solo he podido llegar a una sola conclusión. El criterio muchas veces decidirá cuándo y por qué capturar o no una excepción, y qué hacer con ella. Lamentablemente el criterio no es un bien público.
     
    Dentro de mi experiencia, cuando desarrollé en IC, usábamos mayormente try/finally y rara vez try/catch. Nunca nos interesó capturar una excepción en medio del código con la cual no pudiésemos hacer nada. La excepción subiría hasta la capa de presentación o capa superior en caso de ser un servicio Windows, y loguebamos un error, asíncronamente por supuesto (fire and forget).
     
    En mis viajes por Sudamérica, he visto cada cosa, y lo más triste es que cuando preguntas por qué lo hacen así, no tienen idea. Seguramente picaron código en algún sitio web y creyeron que con eso estaban listos. Un ejemplo típico es:
     
    try
    {
        código
    }
    catch (exception ex)
    {
        throw ex;
    }
     
    Con este código horroroso, además de perder la excepción original, agregas un par de stacks al frame, obligas a realizar muchas más instrucciones de las necesarias.
     
    Criterio, eso es lo que más escasea.
     
    Saludos,
     
    Patrick

  2. Diego dijo:

    Y creame que ni siquiera es patrimonio de Sudamérica hacer mamarrachos con el manejo de excepciones. El ejemplo que yo contaba del robot q vigilaba que los developers capturen las excepciones todas fue en Madrid. Y acá en el fundo de don Sam también he visto cosillas por el estilo
     
    Doble frustración como sudaca que soy me he llevado: no sólo somos truchos para manejar excepciones. Ni siquiera somos truchos originales   :-P
     
    Abrazo, estimado 

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s