lunes, noviembre 29, 2004

Administración de errores (parte 2)

Entre las cosas que no debemos hacer cuando administramos errores están:
C# .NET

try {
  Function1();
} catch {}
Function2();

ASP

on error resume next

Function1()
Function2()


En ambos casos se está enmascarando a los errores y no administrándolos. Si bien esto hará que el usuario no vea ningún error, el peligro es muy grande, tal como se describió en el artículo anterior.

Veamos otro ejemplo de lo que no debemos hacer:
C#.NET

public bool DeleteFile(string fileName) {
  bool canDelete=false;
  try {
    System.IO.File.Delete(fileName);
    canDelete=true;
} catch {}
  return canDelete;
}

ASP

Function DeleteFile(fileName)
  On error resume next
  Dim fso, canDelete
  Set fso = CreateObject("Scripting.FileSystemObject")
fso.DeleteFile(fileName)
  Set fso = Nothing
  canDelete = (Err.number=0)
  Err.Clear
  DeleteFile = canDelete
End Function

Esta es una muy mala práctica. Hemos creado una función que intenta borrar un archivo y devuelve el éxito de la operación. Además de las razones dadas en el ejemplo anterior, acá se agrega que quién llama a la función debe revisar si la función tuvo éxito revisando su valor de verdad. Pero en el caso de que la función devuelva falso, no tenemos ninguna otra información útil que nos sirva para tomar decisiones. No es lo mismo que la función haya fallado porque no tiene permisos a que el archivo no exista.

En el próximo artículo veremos algunas buenas prácticas en la administración de errores.

lunes, noviembre 22, 2004

Administración de errores (parte 1)

Nuestro trabajo exige constantemente tomar decisiones. Una de las más importantes es decidir si vamos a administrar errores o no.
Podemos decidir no hacerlo, de hecho podemos definir, en ASP.NET, una página a la que nos debe redirigir la aplicación en caso de error.
Si decidimos administrar errores debemos saber que es toda una responsabilidad. Pocas cosas suponen un riesgo superior a administrar mal los errores de nuestra aplicación.
Empecemos por el principio (que es, generalmente, el mejor lugar para comenzar). Un error es una excepción que no previmos que podía suceder. Lo mejor y menos peligroso que nos puede suceder cuando se produce un error, es que nuestra aplicación interrumpa su ejecución pues, en caso de continuar, estaría ejecutando en “estado de error” y las consecuencias son difíciles de imaginar.
Un amigo me contó la historia del "Medidor de Presión". Imaginemos una caldera que tiene un medidor de presión. Este tiene unos números sobre un fondo blanco y, a partir de cierto valor, otros con fondo rojo que indica peligro. Cuando la presión supera la línea roja suena la alarma. Cuanto más tiempo transcurra la aguja en la zona de peligro más posibilidades hay de que explote la caldera.
Lo mismo sucede con nuestras aplicaciones. Cuanto más rápido salgamos del "estado de error", menos peligro correremos.

martes, noviembre 16, 2004

Paginas de proceso de datos

En nuestra vida como Desarrolladores Web nos encontraremos muchas veces frente a la tarea de hacer formularios en donde los usuarios deberán ingresar información que será almacenada en una base de datos.

Esta tarea, que es tan sencilla como aburrida, hace caer a muchos en uno de los errores más comunes: el terrible F5 destructor. ¿Qué es eso?

Por lo general, para realizar este trabajo, creamos una página con un formulario cuyo destino es la misma página. De esta forma es mucho más sencillo validar del lado del servidor los datos ingresados y, en caso de error, mostrar un mensaje al usuario manteniendo los datos que ha ingresado previamente.

Hasta acá está todo muy bien, pero he visto muchas veces que una vez guardado los datos, se muestra un mensaje de éxito … ¡en la misma página!
Esto significa que si el usuario presiona F5 y recarga la página, el proceso de guardar los datos se ejecutará nuevamente. Este error es muy común cuando usamos objetos como el DataGrid de .NET.

Imaginemos ahora que en lugar de almacenar los datos, debemos enviar un mail. ¿Qué pasará cuando el usuario presione F5?

El peligro que esto encierra es variado y puede ir desde una sobrecarga en el uso de recursos hasta grabar datos repetidos.

Por extensión, se podría decir que: "Toda página de proceso exitoso debe concluir en un Redirect".

martes, noviembre 09, 2004

Qué y cuándo vs cómo

Ya es hora de que empecemos a tratar cosas un poco más concretas y, para empezar, quiero hablar sobre algo que no sólo agrega "estilo" a nuestro trabajo, también lo hace mucho más mantenible.

En una empresa, existen personas que saben lo que hay que hacer y cuándo hay que hacerlo, y otras que saben cómo hacerlo.

En nuestro código debería haber métodos que sepan qué y cuándo hacer las cosas y métodos que sepan cómo hacerlas. No me parece una buena práctica construir métodos que mezclan ambos roles.

Mantener esa separación nos permite ver rápidamente las reglas de negocio por un lado y, de ser necesario, revisar los algoritmos en forma individual. Además, estaremos creando métodos que podrán ser reutilizados en otras lugares de nuestro desarrollo.

Para graficar un poco más este concepto, veamos un ejemplo en ASPX. Necesitamos un método que envía un mail a la dirección especificada en un formulario. Tenemos un HTML en disco con la etiqueta "$$nombre$$" que debemos reemplazar por el nombre del usuario para personalizar el mail. Finalmente, grabamos el mail en nuestra base de datos.

El que sigue es un ejemplo de código muy poco profesional:
private void Button1_Click(object sender, System.EventArgs e)
  // Configuraciones
  NameValueCollection config = ConfigurationSettings.AppSettings;

  // Leer el archivo desde disco
  string mailFile = config["MailFileName"];
  FileStream fs = new FileStream(mailFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
  StreamReader sr = new StreamReader(fs, Encoding.Default);
  string message = sr.ReadToEnd();

  // Reemplazar etiqueta
  message = message.Replace("$$nombre$$", campoNombre.Text);

  // Enviar mail
  MailMessage mail= new MailMessage();
  mail.Subject = config["MailSubject"];
  mail.From = config["MailFrom"];
  mail.Body = message;
  mail.BodyFormat = MailFormat.Html;
  mail.To = campoEmail.Text;
  SmtpMail.SmtpServer = config["SmtpServer"];
  SmtpMail.Send( mail );

  //Grabar a SQL
  string connString = config["ConnectionString"];
  SqlConnection oConn = new SqlConnection(connString);
  SqlCommand dbCommand = new SqlCommand();
  dbCommand.CommandText = "Exec Insert_Email @email=@@email";
  dbCommand.Connection = oConn;
  int rowsAffected = 0;
  oConn.Open();
  try
  {
    rowsAffected = dbCommand.ExecuteNonQuery();
  }
  catch (Exception ex)
  {
    throw new Exception(ex.Message);
  }
  finally
  {
    oConn.Close();
  }
}

Algo con mucho más estilo sería:
private void Button1_Click(object sender, System.EventArgs e)
{
  // Nunca debemos escribir más código del necesario
  // en los métodos que capturan eventos !!!
  EnviarMailAUsuario(campoNombre.Text, campoEmail.Text);
}
private void EnviarMailAUsuario(string nombre, string email)
{
  //Este metodo sabe ques y cuandos

  NameValueCollection config = ConfigurationSettings.AppSettings;

  // 1.- Leer archivo de disco
  string emailSource = LeerArchivoDeDisco(config["MailFileName"]);

  // 2.- Reemplazar etiqueta
  emailSource = message.Replace("$$nombre$$", campoNombre.Text);

  // 3.- Enviar mail
  EnviarMailDelSitio(campoEmail.Text, emailSource);

  // 4.- Guardar el mail
  GuardarMail(campoEmail.Text);
}

Los métodos invocados seguramente tendrán un código parecido a las líneas correspondientes del primer ejemplo. Noten que no estoy especificando en que capa de mi aplicación se encuentran estos métodos, pero deberían estar en una capa diferente de la de presentación.

viernes, noviembre 05, 2004

Todo es importante

- Lo más difícil de un sitio Web es el diseño. Una vez que tenemos un buen diseño, el resto es hacer que las páginas funcionen. (Un diseñador)
- Una vez que tenemos una aplicación que funciona bien, el resto es ponerle colorcitos. (Un desarrollador)
- Lo verdaderamente complicado es satisfacer los requerimientos del cliente con los recursos que contamos y el tiempo del que disponemos. (Un líder de proyecto)
- Lo importante es conseguir los clientes, la gente para hacer las cosas se consiguen así de fácil. (Un ejecutivo de cuentas)

Señores: todo es igual de importante. Sin clientes no hay desarrollo, sin gente que lidere el proyecto nunca terminaremos en tiempo y forma, un sitio Web gusta por cómo se ve, si el desarrollo funciona mal nada sirve finalmente.

Todos los involucrados en un proyecto deberían trabajar a la par y sin menospreciar el trabajo de los demás.

martes, noviembre 02, 2004

Conocer los riesgos

Si queremos ser desarrolladores de sitios web profesionales debemos conocer los riesgos de lo que hacemos. A mí, particularmente, no me alcanza con que algo "haga lo que tiene que hacer", quiero además que lo haga bien.

Una vez que identificamos la mejor opción para un escenario dado, podemos asumir ciertos riesgos. Pero lo que no debemos hacer es elegir una solución sin estar preparados para lo que pueda suceder de acuerdo a los riesgos de dicha solución.

Supongamos que tenemos que hacer una aplicación web muy sencilla como por ejemplo una serie de páginas que piden datos del usuario y al final los graba en una base. Podríamos tomar la decisión de ir guardando los datos en variables de sesión, pero debemos saber que, si por alguna razón las sesiones se pierden (el sitio está hosteado en más de un servidor, los usuario superan el tiempo de expiración de sesión, etc.), nuestra aplicación dejará de funcionar correctamente o aparecerán errores aparentemente aleatorios.

Como regla general debemos conocer cómo funcionan y que hacen todos aquellos componentes que vayamos a usar en nuestros desarrollos. Personalmente no me gusta utilizar componentes de terceros en aplicaciones críticas a menos que tenga disponible el código fuente.

Finalmente les cuento que mi estrategia (no digo que sea la mejor, es sólo la mía) es siempre comenzar pensando que mi desarrollo estará en múltiples servidores y visitado por muchos usuarios concurrentemente. Esa sería mi aplicación ideal. Luego, en función de los requerimientos reales, puedo ir asumiendo determinados riesgos.