Bugs (des)conocidos

Escribir software libre de errores puede ser virtualmente imposible. Podemos invertir mucho tiempo y esfuerzo en testear nuestra aplicación, realizando todo tipo de verificaciones, y aún así no encontrar el 100% de los defectos que la aplicación tenga.

¿Qué cosas consideramos un defecto? A todo comportamiento no esperado por el sistema, ante cualquier interacción del usuario. Luego, debemos considerar a los diferentes tipos de usuario, incluyendo en éste grupo a los usuarios (llamemos) “malignos”, dedicados a buscar y encontrar vulnerabilidades en el sistema. Organizaciones como mitre, se dedican a investigar y documentar los diferentes tipos de vulnerabilidades más frecuentes, y es muy importante implementar procesos que eviten que éstas ocurran.

Una vez tuve la oportunidad de conversar por algunas horas con Dave Probert, Arquitecto del Kernel de Windows, quien me comentaba que en su equipo se trabaja aún más en definir procesos que eviten la introducción de bugs al sistema, más que a procesos de encontrar problemas en el mismo (lo cual, claro, también se hace). Hacía tanto incapié en esto, comentando que los defectos son responsabilidad de los desarrolladores, aún más que los testers.

Para ello es importante conocer bien las APIs con las que trabajamos y entrenarse en no cometer ninguno de los errores comunes conocidos en el desarrollo de código.

Errores frecuentes conocidos en desarrollo

Debo reconocer que tras recopilar este listado, yo mismo sentí ganas de hacer un code review de cada pieza de software que he escrito. Quienes quieran extender más ejemplos, o compartir sus propias experiencias, bienvenido!

Ignorando excepciones

Nunca, jamas, bajo ningún tipo de circunstancia debe escribirse la siguiente línea de código:

 catch {}

Si atrapamos una Excepción, debemos manejarla correctamente. Si no lo hacemos, debemos dejar que la Excepción siga escalando en nuestro código, hasta que sea manejada. Al hacerlo, siempre deberíamos loguear la excepción, y de ser necesario informar al usuario de una forma amigable, evitando mostrar información privada del sistema (Ejemplo de lo que no hay que hacer, y más de una vez vemos en la UI: “Imposible acceder a la tabla Clientes de la base dbCompras” ).

Olvidando liberar recursos

Utilizar el bloque using para asegurarnos que se invoque al método Dispose al instanciar una clase que implemente la interfaz IDisposable ( :

 using (IDisposable connection = new …) { …
} // Dispose es invocado en este punto, liberando los recursos bloqueados.
 

Chequeando los argumentos

Verificar los argumentos al inicio de cada método no sólo permite explicitar mejor la firma del mismo, sino que al lanzar una ArgumentException, facilitamos la depuración del código ya que se está indicando que el defecto está en la invocación, y no en el evento mismo:

 public void PlaceOrder(Product product, int quantity)
{
    if (product == null)
    {
        throw new ArgumentException(“product no puede ser null”);
    }
    if (quantity < 0)
    {
        throw new ArgumentException(“quantity no puede ser negativo”);
    }

Ignorando los warnings

Los Warnings son una buena fuente de información para encontrar posibles bugs.

image

Como recomendación adicional, FxCop es una herramienta de análisis estático de código, permitiendo encontrar oportunidades de mejoras en diseño, performance, seguridad y localización. Por último, siempre es recomendable activar la opción de compilación “Tratar warnings como errores” al generar los bits para producción:

image

Encodeando valores de salida

En aplicaciones web, siempre que estemos escribiendo sobre el buffer de salida debemos encodear el texto correspondiente. Esto evita ataques de XSS: Si el texto a ser impreso, contuviera código HTML/Javascript maligno, éste sería ejecutado por el navegador. Un ejemplo de código maligno, sería verificar las cookies almacenadas en el navegador del usuario, enviando dicha información a otra fuente, exponiendo datos privados del usuario:

 string htmlEncoded = Server.HtmlEncode(htmlString);
Response.Write(htmlEncoded);

Update: Gracias Matias Iacono por la sugerencia: Microsoft publicó algunos años atrás una biblioteca para prevenir errores de XSS: https://msdn.microsoft.com/en-us/library/aa973813.aspx

Escribiendo errores semánticos

El siguiente error no se dá en lenguajes manejados, ya que el compilador verifica que el tipo de datos sea bool dentro del bloque if. Sin embargo me gustó compartirlo ya que este tipo de convenciones no siempre son tenidas en cuenta, y me gustó compartilo:

 if (a == 2) // Una comparación válida con 2
if (a = 2) // Se produce una asignación, causando un error semántico
if (2 == a) // Convención para causar un error de compilación si = es usado
if (2 = a) // Error de sintáxis

Dejo este post como work in progress. Iré compartiendo nuevos ejemplos, y encantado de recibir los suyos.

Saludos!

Comments

  • Anonymous
    March 08, 2010
    The comment has been removed

  • Anonymous
    March 08, 2010
    Gracias por el comentario Matias! Tampoco incluí las recomendaciones asociadas a prevenir SQL Injection. Me pasó que al empezar a escribir el post, me di cuenta que facilmente se me podía hacer muy extenso el post. Veré si lo voy actualizando, o escribiendo nuevas entradas del tema.

  • Anonymous
    March 08, 2010
    Great post! Agregaría incorporar unit test como parte del proceso de desarrollo.