Archivos mensuales: Febrero 2010

De como Charly cantó sobre software

En su tema Dinosaurios Charly dice:

Los amigos del barrio pueden desaparecer
los cantores de radio pueden desaparecer
los que están en los diarios pueden desaparecer
la persona que amas puede desaparecer.

y sigue en la segunda estrofa:

Los que están en el aire pueden desaparecer, en el aire.
Los que están en la calle pueden desaparecer, en la calle.
Los amigos del barrio pueden desaparecer,
pero los dinosaurios van a desaparecer.

En el último renglón pronostica que los dinosaurios van a desaparecer como han aparecido y desaparecido innumerables dinosaurios en la industria del software.

Por supuesto que no entraré aquí en detalles sobre cuales son “mis” dinosaurios, seria una discusión inútil puesto que cada uno tendrá los suyos.

Lo mas importante viene a continuación:

Si los pesados, mi amor,
llevan todo ese montón
de equipaje en la mano
Oh! mi amor
yo quiero estar liviano.

y remata con el consejo:

Cuando el mundo tira para abajo
es mejor no estar atado a nada,
imaginen a los dinosaurios en la cama.

Aunque no hace falta que el mundo tire para abajo, la enseñanza para los que diseñamos software es bien directa: debemos andar livianos.

Andar livianos en en diseño de software es elegir con criterio y libertad los frameworks que necesitamos y, sobre todo, descartar lo que no necesitamos. Cuanto mas pequeños e independientes mejor.

No recomiendo esos frameworks que arrastran un montón de otros componentes, muchos menos los monolíticos que “solucionan todo”, desde la persistencia hasta la interfaz a usuario.

Andemos livianos colegas, no nos atemos a nada.

Sabiendo que soy mal comentarista, les dejo la letra y la versión cantada por Charly

Nos vemos.

Dependencias ocultas entre clientes y servicios

Hace unos días, en una revisión de código, encontré una definición de interfaz de un servicio que no me gustó. No fue inmediatamente evidente para mi pero uno de sus métodos era un code smell.

Esta es la interfaz:

<span style="color: blue;">public interface </span><span style="color: #2b91af;">IIpStsStore</span>{
    <span style="color: blue;">void </span>AddCredential(<span style="color: blue;">string </span>userName, <span style="color: blue;">string </span>password);
    <span style="color: blue;">void </span>RemoveCredential(<span style="color: blue;">string </span>userName);
    <span style="color: blue;">bool </span>CredentialExist(<span style="color: blue;">string </span>userName);
}

El método que me olía mal es el último. Luego de exprimir el cerebro un poco me di cuenta del motivo. Ese método, a mi juicio, propicia un acoplamiento innecesario entre el cliente del servicio y la implementación de dicho servicio.

Si exponemos ese método seguramente el cliente del servicio lo va a usar y no tendremos control de este uso, es decir, no sabremos exactamente cuando van a utilizarlo.

Supongamos que la primera implementación de ese servicio accede directamente al recurso donde se almacenan las credenciales. En ese caso, si el cliente llama a AddCredential() e inmediatamente llama a CredentialExist(), todo funcionará correctamente.

El problema aparecerá cuando hagamos otra implementación del servicio IpStsStore que funcione en forma asíncrona. En ese caso ya no habrá garantía de que la secuencia de métodos mencionada mas arriba siga funcionando.

En definitiva, tenemos un acoplamiento entre el cliente y la implementación del servicio, algo que siempre debe tratar de evitarse, además, como suele ocurrir en estos casos, luego de pensar un poco el diseño, ese método tampoco era necesario, un típico caso YAGNI.

Que nunca falte un registro de actividad en tu aplicación. Nunca.

La semana pasada un cliente me llamó debido a que una de sus aplicaciones recién lanzada estaba un tanto inestable. Quiero compartir hoy con ustedes la recomendación que le di, la mas simple y mas efectiva que recuerdo haber dado.

Mas allá de revisar algunas cuestiones directamente relacionadas con las inestabilidades reportadas por usuarios finales, lo primero que les pedí es que me mostraran los logs para enterarme de que no los tenían.

Hoy no hay excusas para no tener información de este tipo en la plataforma .NET, todos los tipos de aplicaciones (Web, Desktop, Servicios, etc.) proveen un mecanismo centralizado de

Aquí algunos consejos básicos:

  • Registren todas las excepciones (básico)
  • No es necesario utilizar try – catch innecesariamente en todo su código, es suficiente con engancharse en el handler centralizado.
  • Registre información con distinto nivel de detalle (los niveles corresponden a log4net pero los demás utilitarios tienen niveles similares), al menos:
    • ERROR: las excepciones, deje que el framework se encargue de mostrar mensajes, callstacks, etc. También registre errores de formato, de protocolo, etc, aunque también podría utilizarse el nivel WARNING (o WARN).
    • INFO: algunos puntos importantes del funcionamiento de la aplicación tales como el inicio, datos de configuración utilizados, finalización, etc.
    • DEBUG: en esta modalidad puede registrarse información mas detallada del funcionamiento de la aplicación.
  • Configure correctamente el nivel de registro en cada ambiente. Generalmente uso nivel INFO en ambientes de producción (que también registra WARN, ERROR y CRITICAL) y nivel DEBUG en ambientes de desarrollo.
  • Use herramientas sencillas (log4net, NLog) y registre la información en simple archivos de texto. Nunca me resulto cómodo utilizar el registro de eventos de Windows.

Para aplicaciones ASP.NET

<span style="color: blue;">protected void </span>Application_Start()
{
    Error += Application_Error;
}

<span style="color: #0000ff; font-size: x-small;"><span style="color: #0000ff; font-size: x-small;">private</span></span><span style="font-size: x-small;"> </span><span style="color: #0000ff; font-size: x-small;"><span style="color: #0000ff; font-size: x-small;">void</span></span><span style="font-size: x-small;"> Application_Error(    </span><span style="color: #0000ff; font-size: x-small;"><span style="color: #0000ff; font-size: x-small;">object</span></span><span style="font-size: x-small;"> sender, </span><span style="color: #2b91af; font-size: x-small;"><span style="color: #2b91af; font-size: x-small;">EventArgs</span></span><span style="font-size: x-small;"> e)

{
</span>
<blockquote><span style="font-size: x-small;">
</span><span style="color: #2b91af; font-size: x-small;"><span style="color: #2b91af; font-size: x-small;">Exception</span></span><span style="font-size: x-small;"> ex = Server.GetLastError();
</span></blockquote>
<span style="font-size: x-small;">
</span>
<blockquote><span style="font-size: x-small;">
log.Error(</span><span style="color: #a31515; font-size: x-small;"><span style="color: #a31515; font-size: x-small;">"Application_Error", ex</span></span><span style="font-size: x-small;">);
</span></blockquote>
<span style="font-size: x-small;">
<blockquote>
Server.ClearError();</blockquote>
}

</span>

Para aplicaciones Desktop

<span style="color: blue;">static void </span>Main()
{
<span style="color: #2b91af;"><span style="color: #333333;">    </span>AppDomain</span>.CurrentDomain.UnhandledException +=
        UnhandledExceptionHandler;

    <span style="color: #2b91af;">Application</span>.Run(<span style="color: blue;">new </span><span style="color: #2b91af;">Principal</span>());
}
<span style="color: blue;">private static void </span>UnhandledExceptionHandler(
    <span style="color: blue;">object </span>sender, <span style="color: #2b91af;">UnhandledExceptionEventArgs </span>args)
{
    <span style="color: blue;">var </span>e = (<span style="color: #2b91af;">Exception</span>)args.ExceptionObject;

    log.Error(<span style="color: #a31515; font-size: x-small;"><span style="color: #a31515; font-size: x-small;">"Application_Error", ex</span></span><span style="font-size: x-small;">);

</span>    <span style="color: blue;">if </span>(<span style="color: #2b91af;">MessageBox</span>.Show(<span style="color: #a31515;">"Reporte este error:\n" </span>+         e + <span style="color: #a31515;">"\n\n¿Desea copiarlo al portapapeles?"</span>,
        <span style="color: #a31515;">"Error en Clasificador"</span>,
        <span style="color: #2b91af;">MessageBoxButtons</span>.YesNo, <span style="color: #2b91af;"><span style="color: #333333;">        </span>MessageBoxIcon</span>.Error) == <span style="color: #2b91af;">DialogResult</span>.Yes)
    {
        <span style="color: #2b91af;">Clipboard</span>.SetText(e.ToString());
    }
    <span style="color: #2b91af;">Environment</span>.Exit(1);
}

Mas informacion: