Introducción
Este documento describe reglas y recomendaciones para el desarrollo de aplicaciones y librerías de clases usando el lenguaje C#. La meta es definir líneas guías para enmarcar la consistencia de estilo y de formato y ayudar a los desarrolladores a evadir errores comunes.
A pesar de ser un tema complejo, crear estándares para la programación puede ser de mucha utilidad para desarrolladores dentro de la organización, es por eso por lo que estos estándares deben tender a lo práctico.
Las técnicas mencionadas en el presente documento no pretenden forman conjuntos inflexibles de estándares, más bien, intenta servir de guía en el desarrollo de un estándar para los proyectos del área de desarrollo de DigiPro.
La utilización de estándares debe mostrar efectos positivos sobre:
- Disminución de errores / bugs, especialmente en aquellos difíciles de individualizar.
- Mantenimiento del código estructurado de acuerdo con las guías de diseño.
- Mantenimiento del código escrito de acuerdo con un estilo estandarizado.
- Performance de la aplicación desarrollada con prácticas eficientes del lenguaje.
La información aquí contenida proviene de varias fuentes, las cuales se listan en el Anexo 1: Bibliografía consultada.
Alcance
Este documento aplica a los proyectos de la empresa DigiPro Sa de CV, desarrollado con Visual Studio .Net, lenguaje C#. Abarca los conceptos de nomenclaturas, definición de variables, métodos, manejo de errores y comentarios, así como algunas buenas prácticas de programación.
Específicamente, este documento contiene:
- Convenciones de nomenclatura
- Estilo de codificación
- Uso del lenguaje
- Diseño de modelos de objetos.
Técnicas de codificación
Convenciones y estándares de nombres
El esquema de nombres es una de las ayudas más importantes para entender el flujo lógico de una aplicación. Un nombre debe más bien expresar el “qué” que el “cómo”. Si evita nombres que se refieran a la implementación subyacente (sujeta a cambios), estará conservando un grado de abstracción que lo simplificará todo. Por ejemplo, puede usar GetNextStudent() en vez de GetNextArrayElement().
Nombrar proyectos, archivos de código fuente, e identificadores incluyendo campos, variables, propiedades, métodos, parámetros, clases, interfases y espacios de nombres (Namespaces).
Prefijo de controles
Prefijo (minúsculas) | Objeto/Control |
bto | Boton |
txt | TextBox |
grv | GridView |
lbl | Label |
lst | ListBox |
cr | CrystalReportViewer |
wf | WebForm |
ddwn | DropDownList |
Sufijo de las clases
- Todos los nombres de las entidades del negocio deben llevar el prefijo be seguido de un nombre que la defina claramente con la primera letra en mayúsculas. Ejm: beEquipo.
- Los nombres de clases de la lógica del negocio llevaran el prefijo bl seguido de un nombre que empiece con mayúsculas y que la identifique claramente. Ejm: blEquipo.
- Las clases de acceso a datos serán nombradas con el prefijo dal seguido de un nombre que empiece con mayúsculas y que la identifique claramente. Ejm: dalEquipo.
- Los formularios web empezaran con el sufijo wf seguido de un nombre que describa el caso de uso que representa. Ejm: wfRegistrarEquipo.
Técnicas de nomenclatura recomendadas
- Evitar nombres totalmente en MAYÚSCULAS o en minúsculas a menos que estos sean de una sola palabra.
- Nunca usar nombres que comiencen con caracteres numéricos.
- Todos los nombres por utilizar deben ser específicos y deben tener el largo necesario para ser entendidos.
- Evitar el uso de abreviaturas a menos que el nombre completo sea excesivo. En los casos necesarios, las abreviaturas deben ser totalmente conocidas y aprobadas por todos los integrantes.
- Nunca usar palabras reservadas para nombres.
- Minimice el uso de abreviaturas; pero si las emplea, use coherentemente las que haya creado. Una abreviatura sólo debe tener un significado y, del mismo modo, a cada palabra abreviada sólo debe corresponder una abreviatura. Por ejemplo, si utiliza “min.” para abreviar “mínimo”, hágalo siempre así, y no use también “min.” para abreviar “minuto”.
- Cuando ponga nombre a las funciones, incluya una descripción del valor que vaya a ser devuelto, como por ejemplo GetCurrentWindowName().
- Los archivos y los nombres de carpetas, al igual que los nombres de procedimientos, deben describir claramente su finalidad.
- Evite reutilizar nombres para elementos diferentes, como por ejemplo una rutina llamada ProcessSales() y una variable iProcessSales.
- Evite los homónimos, como write y right, para evitar confusiones durante las revisiones del código.
- Al dar nombre a los elementos, evite las palabras mal escritas comúnmente. Tenga también cuidado con las diferencias que existen entre las diferentes variedades regionales del idioma.
- Evite las marcas tipográficas para identificar tipos de datos, como $ para las cadenas, o % para los enteros.
Variables
Los nombres de variables iniciarán con la descripción del tipo que las contendrá seguido de un nombre que las especifique claramente sin llegar a ser demasiado extenso.
- Añada calificadores de computación (Avg, Sum, Min, Max, Index) después de un nombre de variable donde le resulte apropiado.
- Utilice pares complementarios en nombres de variables, como min/max, begin/end, y open/close.
- Dado que la mayoría de nombres se construyen concatenando varias palabras, emplee una mezcla de mayúsculas y minúsculas para simplificar la lectura. Además, para ayudar a distinguir entre variables y rutinas, utilice el método Pascal de mayúsculas y minúsculas (CalculateInvoiceTotal) para los nombres de rutinas, en el que la primera letra de cada palabra está en mayúscula. Para las variables, ponga la primera letra de cada palabra en mayúscula, exceptuando la primera (documentFormatType).
- Los nombres de variables booleanas deberían contener Is, lo que implica valores del tipo Yes/No o True/False, como por ejemplo fileIsFound.
- Evite usar términos del tipo Flag cuando ponga nombre a variables de estado, que difieren de las variables booleanas en que aquéllas deben tener más de dos valores posibles. En vez de documentFlag, utilice un nombre más descriptivo, del tipo documentFormatType.
- Incluso para el caso de una variable de poco uso, que deba aparecer sólo en unas cuantas líneas de código, emplee un nombre descriptivo. Utilice nombres de variables de una sola letra, como i o j sólo para índices cortos.
- No utilice números o cadenas literales, como por ejemplo For i = 1 To 7. En su lugar, emplee constantes con nombre, del tipo For i = 1 To NUM_DAYS_IN_WEEK para que resulten fáciles de mantener y comprender.
- No uses notación Húngara para el nombre de las variables.
- En épocas previas muchos de los programadores les agradaba la notación Húngara – la cual especifica el tipo de dato de la variable como un prefijo en el nombre y usar el prefijo m_ para variables globales.
Ejemplo:
string m_sNombre;
int nEdad;
Sin embargo, en los estándares de codificación de .NET, esto no es recomendado. El uso del tipo de dato y del prefijo m_ para representar variables globales no debe ser usado. Todas las variables deben usar una notación Camell.
Aun así, algunos programadores prefieren usar el prefijo m_ para representar variables globales dado que no hay otra forma fácil de identificar una variable global.
- Usa palabras entendibles y descriptivas para nombrar a las variables. No uses abreviaciones.
Correcto:
string direccion;
int salario;
Incorrecto:
string nom;
string domic;
int sal;
- No uses nombres de variables de un solo caracter como i, n, s etc. Usa nombres como índice, temp.
Una excepción en este caso podría ser las variables usadas para iteraciones en los ciclos:
for ( int i = 0; i < cuantos; i++)
{
…
}
Si la variable es usada solo como un contador para una iteración y no es usada en ningún otro lado dentro del ciclo, mucha gente le agrada nombrar la variable con un solo caracter (i) en vez de usar un nombre adecuado distinto.
Estilo de codificación – formato
- Nunca declarar más de un espacio de nombres por archivo.
- Evitar colocar múltiples clases en un mismo archivo.
- Siempre colocar llaves (“{“ y “}”) en líneas separadas a excepción de cuando se declaran las propiedades Get y Set.
- Declarar cada variable independientemente – nunca en la misma sentencia.
- Colocar la sentencia “using” en la parte superior del archivo. El grupo de los espacios de nombres de .NET colocarle por encima de los espacios de nombres particulares y todos en ordenados alfabéticamente.
Ejemplo:
using System;
using System.Collections
using System.Data;
using System.Data.SqlClient;
using System.Xml;
using Gustozzi.BE;
using Acceso_a_Datos;
- Establezca un tamaño estándar de sangría (por ejemplo, cuatro espacios) y úselo siempre. Alinee las secciones de código mediante la sangría predeterminada.
- La sangría aplicada al código hace que éste sea más fácil de leer, como por ejemplo en:
If ... Then
If ... Then
...
Else
...
End If
Else
...
End If
- Use un único tipo de letra cuando publique versiones impresas del código fuente.
- Alinee verticalmente las llaves de apertura y cierre donde los pares de llaves se alinean, como por ejemplo en:
for (i = 0; i < 100; i++)
{
...
}
Ó
for (i = 0; i < 100; i++){
...
}
Sea cual sea el estilo que escoja, úselo siempre en todo el código fuente.
Estilo de codificación – comentarios
- Usar // o /// pero NO /* … */
- No utilizar marcos de asteriscos.
Ejemplo:
//******************************************
// Cuadro de comentarios
//******************************************
- Tratar de usar comentarios en el interior de los métodos para explicar aspectos globales del algoritmo.
- No utilizar comentarios para explicar código obvio.
- Si programa en C#, utilice la función de documentación XML. Para obtener más información, vea Documentación XML.
- Cuando modifique el código, mantenga siempre actualizados los comentarios circundantes.
- Al principio de cada rutina, resulta útil hacer comentarios estándar, repetitivos, que indiquen el propósito de la rutina, las suposiciones y las limitaciones. Un comentario repetitivo podría consistir en una breve introducción que explicara por qué existe y qué puede hacer.
- Evite añadir comentarios al final de una línea de código, porque lo hacen más difícil de leer. Sin embargo, los comentarios de final de línea sí son apropiados al anotar declaraciones de variables. En este caso, alinee todos los comentarios de final de línea en la misma posición de tabulación.
- Evite los comentarios recargados, como las líneas enteras de asteriscos. En su lugar, utilice espacios para separar los comentarios y el código.
- Evite rodear un bloque de comentarios con un marco tipográfico. Puede resultar agradable, pero es difícil de mantener.
- Antes de la implementación, quite todos los comentarios temporales o innecesarios, para evitar cualquier confusión en la futura fase de mantenimiento.
- Si necesita realizar comentarios para explicar una sección de código compleja, examine el código para decidir si debería volver a escribirlo. Siempre que sea posible, no documente un código malo, vuelva a escribirlo. Aunque, por regla general, no debe sacrificarse el rendimiento para hacer un código más simple para el usuario, es indispensable un equilibrio entre rendimiento y mantenimiento.
- Use frases completas cuando escriba comentarios. Los comentarios deben aclarar el código, no añadirle ambigüedad.
- Vaya comentando al mismo tiempo que programa, porque probablemente no tenga tiempo de hacerlo más tarde. Por otro lado, aunque tuviera oportunidad de revisar el código que ha escrito, lo que parece obvio hoy es posible que seis semanas después no lo sea.
- Evite comentarios superfluos o inapropiados, como comentarios divertidos al margen.
- Evite poner número de Osis e iniciales del programador,
- Evite poner el nombre del cliente o datos sensible en los archivos config, comentarios en el código, nameSpace y/o componentes de desarrollo como dll, setups, etc.
- Use los comentarios para explicar el propósito del código. No los use como si fueran traducciones interlineales.
- Comente cualquier cosa que no sea legible de forma obvia en el código.
- Para evitar problemas recurrentes, haga siempre comentarios al depurar errores y solucionar problemas de codificación, especialmente cuando trabaje en equipo.
- Haga comentarios en el código que esté formado por bucles o bifurcaciones lógicas. Se trata en estos casos de áreas clave que ayudarán a los lectores del código fuente.
- Realice los comentarios en un estilo uniforme, respetando una puntuación y estructura coherentes a lo largo de toda la aplicación.
- Separe los comentarios de sus delimitadores mediante espacios. Si respeta estas normas, los comentarios serán más claros y fáciles de localizar si trabaja sin indicaciones de color.
Uso del lenguaje
Nunca omitir los modificadores de acceso. Declare explícitamente todos los métodos con su apropiado modificador de acceso en lugar de dejarlo con el de por defecto. Ejemplo:
// Correcto!
void Escribir_Evento(string mensaje)
{…}
// Incorrecto!
private void Escribir_Evento(string mensaje)
{…}
Tratar de inicializar las variables cuando las declare.
string strVariable System.String
- Evitar declarar variables de cadena con valores literales. En lugar use constantes, recursos, registros de sistema u otro tipo de repositorio de datos.
- Solo declarar constantes para tipos de datos simples.
Siempre inicializar explícitamente los tipos referenciados en un arreglo al recorrer un bucle
Control de excepciones
- No usar los bloques try/catch para control de flujos.
- Solo capturar las excepciones cuando van a ser controladas.
- Nunca declarar un bloque match vacío.
- Evitar anidar bloques try/catch en otro bloque catch.
- Ordenar los filtros de excepciones de más específica a más genérica.
- Evitar el relanzamiento de excepciones.
- Solo usar el bloque finally para liberar recursos utilizados en el bloque try
- Siempre utilizar validaciones para evitar excepciones.
- Nunca haga una “‘catch exception y no hacer nada”. Si oculta una excepción, nunca se sabe si la excepción sucedió o no. Muchos desarrolladores utilizan este método práctico para ignorar errores no significativos. Usted siempre debe tratar de evitar las excepciones mediante la comprobación de todas las condiciones de error de programación. En cualquier caso, la captura de una excepción y no hacer nada no es permitido. En el peor de los casos, se debe registrar la excepción y continuar.
- En el caso de las excepciones, dar un mensaje amigable para el usuario, pero registrar el error real con todos los detalles posibles sobre el error, incluyendo el momento en que ocurrió, el método y el nombre de clase, etc.
- Siempre cachar sólo la excepción específica y no genérica.
Correcto:
void ReadFromFile (string fileName )
{
try
{
// read from file.
}
catch (FileIOException ex)
{
// log error.
// re-throw exception depending on your case.
throw;
}
}
Incorrecto:
void ReadFromFile (string fileName)
{
try
{
// read from file.
}
catch (Exception ex)
{
// Catching general exception is bad... we will never know whether
// it was a file error or some other error.
// Here you are hiding an exception.
// In this case no one will ever know that an exception happened.
return "";
}
}
- No hay necesidad de capturar la excepción general en todos sus métodos. Déjela abierta y dejar que el bloqueo de la aplicación. Esto le ayudará a encontrar la mayoría de los errores durante el ciclo de desarrollo. Usted puede tener un nivel de aplicación (nivel de rosca) controlador de errores donde se pueden controlar todas las excepciones generales. En el caso de un “error inesperado general”, este controlador de error debe detectar la excepción y debe registrar el error, además de dar un mensaje amistoso al usuario antes de cerrar la aplicación, o permitiendo al usuario “ignorar y continuar ‘.
- Cuando se vuelva a producir una excepción, utilice la instrucción throw sin especificar la excepción original. De esta manera, la pila de llamadas original se conserva.
Good:
catch
{
// do whatever you want to handle the exception
throw;
}
Not Good:
catch (Exception ex)
{
// do whatever you want to handle the exception
throw ex;
}
- No escriba muy grandes bloques try-catch. Si es necesario, escriba aparte try-catch para cada tarea a realizar y encerrar sólo la parte específica del código dentro del try-catch. Esto le ayudará a encontrar qué parte del código generado la excepción y se puede dar el mensaje de error específicas para el usuario.
- Escriba sus propias clases de excepción personalizadas si se requiere en su aplicación. No derive sus excepciones personalizadas a partir de la clase SystemException base. En su lugar, se heredan de ApplicationException.
Guía rápida
En esta sección se incluye un breve resumen de los principales estándares descriptos a los largo de este documento. Estas tablas no son detalladas en sus descripciones, pero brindan una rápida referencia a los elementos.
Convenciones de nomenclatura
c | Camel case |
P | Pascal case |
_ | Prefijo con infraguión (underscore) |
X | No aplica |
Estilos de codificación
Código | Estilo |
Archivo del código fuente (Source code) | Un archivo por Namespace y un archivo por clase. |
Llaves {} | Siempre en una nueva línea. Utilizar paréntesis cuando sea opcional. |
Tabulación (Indent) | Utilizar siempre tabs de 4 caracteres. No utilizar espacios en blanco. |
Comentarios (Comments) | Utilizar “//”. No utilizar “/*…*/”. |
Variables | Una variable por cada declaración. |
Tipos de datos nativos (Native Data Types) | Priorizar el uso de tipos nativos de C# ante tipos del CTS. Ejemplo: int en lugar de Int32. |
Enumerados (Enums) | Evitar cambiar el tipo de dato predeterminado. |
Tipos genéricos (Generic types) | Utilizar preferiblemente tipos genéricos ante tipos estándar o strong-typed classes. |
Propiedades (Properties) | No utilizar prefijos Get / Set. |
Métodos (Methods) | Utilizar un máximo de 7parámetros. |
Base / This | Utilizar solo en constructors o dentro de override. |
Condiciones ternarias (Ternary conditions) | |
For each | No modificar tipos enumerados dentro de una sentencia foreach. |
Condicionales (Conditionals) | Evitar la evaluación de condiciones booleanas contra valores true o false. No realizar asignaciones embebidas. No utilizar invocación de métodos embebidos. |
Manejos de excepciones (Exception Handling) | No utilizar excepciones para el control de flujo. Utilizar throw; NO throw e; cuando se redirije. Sólo utilizar catch cuando es posible manejar la excepción. Utilizar validaciones para evitar la ocurrencia de excepciones. |
Eventos (Events) | Siempre debe validarse el valor null antes de la invocación. |
Bloqueo (Locking) | Utilizar lock() en lugar de Monitor.Enter() No utilizar lock en un tipo, por ejemplo: lock(typeof(MyType)); Evitar el uso de lock ante un this, por ejemplo: lock(this); Preferiblemente, utilizar lock en objetos declarados como private, por ejemplo: lock(myVariable); |
Dispose() / Close() | Utilizarlo siempre que sea possible. |
Finalizers | Evitar la implementación de estos métodos. Utilizar la sintaxis del destructor de C#. Ejemplo: ~MyClass() {…} Nunca debe definirse un método llamado Finalize(); |
AssemblyVersion | Incrementar en forma manual. |
ComVisibleAttribute | Debe estar con el valor false para todos los assemblies. |
Buenas prácticas de programación
1. Evita escribir métodos muy largos. Un método debe típicamente tener entre 1 a 25 líneas de código. Si un método tiene más de 25 líneas de código, debes considerar refactorizarlo en métodos separados.
2. El nombre de los métodos debe decir lo que hace. No uses nombres engañosos. Si el nombre del método es obvio, no hay necesidad de documentación que explique qué hace el método.
Correcto:
void GuardaNumeroTelefonico ( string numeroTelefono )
{
}
Incorrecto:
//Este método guardará el número de teléfono
void GuardaDetalles ( string num )
{
//Guarda el número de teléfono.
}
3. Un método debe tener solo ‘una tarea’. No combines más de una tarea en un solo método, aún si esas tareas son pequeñas.
Correcto:
//Guarda la dirección
//Envia un email para informar que la dirección es actualizada.
SetDirección ( direccion );
EnviaCorreoAlSupervisor( direccion, email );
void SetDirección ( string direccion )
{
// Guarda la dirección.
// …
}
void EnviaCorreoAlSupervisor ( string dirección, string email )
{
// Envia un correo para informar que la dirección fue cambiada.
// …
}
Incorrecto:
// Gurada la dirección y envía un correo al supervisor para informar que
// la dirección fue cambiada.
GuardaDireccion ( direccion, email );
void GuardaDireccion ( string direccion, string email )
{
// Tarea 1.
// Guarda la direccion.
// …
// Tarea 2.
// Envia un correo para informar al supervisor que la direccion fue cambiada
// …
}
4. Usa los tipos específicos de C# o VB.NET (alias), en vez de los tipos definidos en el espacio de nombres System.
int edad; (no Int16)
string nombre; (no String)
object infoContrato; (no Object)
5. Siempre verifica valores inesperados. por ejemplo, si estas usando un parámetro con 2 posibles valores, nunca asumas que si uno no concuerda entonces la única posibilidad es el otro valor.
Correcto:
If ( tipoMiembro == eTipoMiembro.Registrado )
{
// Usuario registrado… haz algo…
} else if ( tipoMiembro == eTipoMiembro.Invitado )
{
// Usuario invitado… haz algo…
}
else
{
// Usuario inesperado. Lanza una excepción
throw new Exception ( “Valor inesperado “ + tipoMiembro.ToString() + “.” )
// si nosotros agregamos un nuevo tipo de usuario en el futuro,
// podremos fácilmente encontrar el problema aquí.
}
Incorrecto:
If ( tipoMiembro == eTipoMiembro.Registrado )
{
// Usuario registrado… haz algo…
}
else
{
// Usuario invitado… haz algo…
// Si nosotros introducimos otro tipo de usuario en el futuro,
// este código fallará y no se notará.
}
6. No incrustes números en el código. En vez de eso usa constantes. Declara constantes en la parte superior del archivo y úsalas en tu código. Sin embargo, usar constantes tampoco es recomendado . Se debe usar las constantes en el archivo de configuración o en la base de datos, de tal forma que las puedas cambiar posteriormente. Declara los valores como constantes solo si tu estas totalmente seguro de que este valor nunca necesitará ser cambiado.
7. No incrustes cadenas de texto en el código. Usa archivos de recursos.
8. Convierte las cadenas de texto a minúsculas o mayúsculas antes de compararlas. Esto asegurará que la cadena coincida.
if ( nombre.ToLower() == “juan” )
{
// …
}
9. Usa String.Empty en vez de “”.
Correcto:
if ( nombre == String.Empty )
{
// haz algo
}
Incorrecto:
if ( nombre = “” )
{
// haz algo
}
10. Evita usar variables globales. Declara variables locales siempre que sea necesario y pásalas a otros métodos en vez de compartir una variable global entre métodos. Si compartes una variable global entre métodos, te será difícil rastrear qué método cambia el valor y cuando.
11. Usa enum dondequiera que sea requerido. No uses números o cadenas para indicar valores discretos.
Correcto:
enum TipoCorreo
{
Html,
TextoPlano,
Adjunto
}
void EnviarCorreo (string mensaje, TipoCorreo tipoCorreo)
{
switch ( tipoCorreo )
{
case TipoCorreo.Html:
// Haz algo
break;
case TipoCorreo.TextoPlano:
// Haz algo
break;
case TipoCorreo.Adjunto:
// Haz algo
break;
default:
// Haz algo
break;
}
}
Incorrecto:
void EnviarCorreo (string mensaje, string tipoCorreo)
{
switch ( tipoCorreo )
{
case “Html”:
// Haz algo
break;
case “TextoPlano”:
// Haz algo
break;
case “Adjunto”:
// Haz algo
break;
default:
// Haz algo
break;
}
}
12. No definas las variables globales públicas o protegidas. Mantenlas privadas y expón Propiedades públicas/protegidas.
13. Las rutinas que controlan los eventos (event handlers) no deben contener el código que ejecuta la acción requerida. En vez de ello, llama a otro método desde la rutina controladora.
ButtonVentas_Click(Object sender, EventArgs e) Handles Button1.Click
{
GetVentasPorMes();
}
14. No invoques programáticamente el evento Click de un botón para ejecutar la misma acción que has escrito en el evento. En vez de ello, llama el mismo método que es invocado desde la rutina controladora del evento click.
15. Nunca incrustes en el código rutas o letras de dispositivos. Obtén la ruta de la aplicación programáticamente y usa rutas relativas a ella.
16. Nunca asumas que tu código se ejecutará desde el disco “C:”. No puedes saber si algunas usuarios lo ejecutan desde la red o desde “Z:”.
17. En el arranque de la aplicación, ejecuta una clase de “auto verificación” y asegúrate que todos los archivos requeridos y las dependencias están disponibles en las ubicaciones esperadas. Verifica las conexiones a la base de datos en el arranque, si es requerido. Dale un mensaje amigable al usuario en caso de algún problema.
18. Si el archivo de configuración requerido no se encuentra, la aplicación debe ser capaz de crear uno con valores predeterminados.
19. Los mensajes de error deben ayudar al usuario a resolver el problema. Nunca muestres mensajes de error como “Error en la Aplicación”, “Hay un error…” etc. En vez de ello da mensajes específicos como “Fallo al actualizar la base de datos”, sugiere lo que el usuario debe realizar: “Fallo al actualizar la base de datos. Por favor asegúrate de que la cuenta y la contraseña sean correctos”.
20. Cuando despliegues mensajes de error, adicionalmente a decirle al usuario qué está mal, el mensaje debe también decirle lo que el usuario debe hacer para resolver el problema. En vez de un mensaje como “Fallo al actualizar la base de datos.”, sugiere lo que el usuario debe hacer: “Fallo al actualizar la base de datos. Por favor asegúrate de que la cuenta y la contraseña sean correctos.”.
21. Muestra mensajes cortos y amigables al usuario. Pero registra el error actual con toda la información posible. Esto ayudará mucho a diagnosticar problemas.
22. Usa archivos de recursos (.resx) para todos los literales de la aplicación. Nos favorece el cambio idiomático, cambio de literales rápido, unificación de mensajes de eventos, etc. En desarrollos muy compartidos, pensar en encapsular en una(s) librería(s) independiente(s) los literales y mensajes para que pueda ser fácilmente compartido por las demás librerías.
¿y por qué no hacer un sistema basado XML ó Tablas en BBDD?… nadie te lo impide. Pero ten en cuenta que un archivo.resx es un sistema “específico” de .NET ideado para esta función y por tanto muy optimizado y “precompliado” (por lo tanto más rápido de acceder). XML es más lento y en cadenas largas muy poco optimo.
23. No guardes más de una clase en un solo archivo.
24. Evita tener archivos muy grandes. Si un solo archivo tiene más de 1000 líneas de código,
es un buen candidato para refactorizar. Divídelos lógicamente en dos o más clases.
25. Evita métodos y propiedades públicas, a menos que ellas realmente necesiten ser accedidas desde afuera de la clase. Usa “internal” si ellas solo son accedidas dentro del mismo ensamblado. Normalmente pocos usan esta buena práctica, es muy recomendable pero por otro lado es menos “ágil” al codificar.
internal class Test
{
public int MiTest;
}
Otro ejemplo completo:
// Assemby : A
namespace AssemblyA
{
public class A
{
protected internal string SomeProtectedInternalMethod()
{
return "SomeValue";
}
}
public class A2 : A
{
public string SomeMethod()
{
// We can access the method because
// it's protected and inherited by A2
return SomeProtectedInternalMethod();
}
}
class A3 : A
{
public string SomeMethod()
{
A AI = new A();
// We can access the method through an instance
// of the class because it's internal
return AI.SomeProtectedInternalMethod();
}
}
}
// Assemby : B
using AssemblyA;
namespace AssemblyB
{
class B : A
{
public string SomeMethod()
{
// We can access the method because
// it's inherited by A2
// despite the different assembly
return SomeProtectedInternalMethod();
}
}
class B2
{
public string SomeMethod()
{
A AI = new A();
// We can't access the method
// through the class instance
// because it's different assembly
return AI.SomeProtectedInternalMethod();
}
}
}
26. Evita pasar muchos parámetros a un método. Si tienes más de 4~5 parámetros, es un buen candidato para definir una clase o una estructura. Lo contrario destroza el consumo en memoria, más facilidad de corrupción de datos, más castigo a los ciclos del procesador.
27. Si tienes un método que retorna una colección, devuelve una colección vacía en vez de null, si no hay datos que retornar. Por ejemplo, si tienes un método que retorna un ArrayList, siempre retorna un ArrayList válido. Si no tienes elementos que devolver, entonces retorna un ArrayList válido con 0 elementos. Esto hará fácil para la aplicación que llama al método verificar solamente la propiedad “Count” en vez que hacer verificaciones adicionales para “null”.
28. Usa el archivo AssemblyInfo para llenar la información como el número de versión, descripción, nombre de la compañía, nota de derechos reservados etc.
29. Organiza lógicamente tus archivos dentro de carpetas apropiadas. Usa una jerarquía de carpetas de 2 niveles. Puedes tener hasta 10 carpetas en raíz y cada carpeta hasta 5 subcarpetas. Si tienes muchas carpetas que no pueden ser acomodadas en la jerarquía de 2 niveles mencionada arriba, necesitas refactorizar en distintos ensamblados.
30. Asegúrate de tener una buena clase de registro (logging) la cual puede ser configurada para registrar errores, advertencias o trazar acciones. Si configuras para registrar errores, deben ser sólo errores. Pero si la configuras para registrar las acciones, debe registrar todo (errores, advertencias y acciones). Tu clase de registro debe ser escrita de tal manera que en un futuro puedas cambiar fácilmente para trazar en el Registro de Eventos de Windows, SQL Server o enviar Correo Electrónico al administrador o a un Archivo etc sin cambiar nada en otras partes de la aplicación. Usa la clase de registro extensivamente en el código para registrar errores, advertencias y aún registrar mensajes de actividades que puedan ayudar a resolver un problema. 31. Si estas abriendo conexiones a una base de datos, sockets, archivos etc, siempre cierra dichas conexiones en el bloque finally. Esto asegurará que aún si una excepción ocurre después de abrir la conexión, se cerrará seguramente en el bloque finally.
int numerillo = 123;
string texto = "cosillas";
object oobjetoo = s;
try
{
// Invalid conversion; o contains a string not an int
numerillo = (int) oobjetoo ;
}
finally
{
Console.Write("numerillo = {0}", numerillo );
}
32. Declara variables tan cerca como sea posible de donde son usadas por primera vez. Usa una declaración de variable por línea.
33. Usa la clase StringBuilder en vez de String si tienes que manipular objetos de tipo string dentro de un ciclo. El objeto String trabaja en modo extraño en .NET. Cada vez que concatenas una cadena, realmente se descarta la cadena anterior y se recrea un nuevo objeto, lo cual es una operación relativamente pesada.
Considera el siguiente ejemplo:
public string ComponMensaje ( string[] lineas )
{
string mensaje = string.Empty;
for ( int i = 0; I < lineas.Lenght; i++ )
{
mensaje += lineas[i]
}
return mensaje;
}
En el ejemplo de arriba, podría parecer que solo estamos concatenando al objeto string ‘mensaje’. Pero lo que realmente está sucediendo es que, el objeto string se descarta en cada iteración y recreado con la línea agregada a él.
Si tu ciclo tiene bastantes iteraciones, entonces es una buena idea usar la clase StringBuilder en vez del objeto String.
Observa el ejemplo donde el objeto String es reemplazado con StringBuilder.
public string ComponMensaje ( string[] líneas )
{
StringBuilder mensaje = new StringBuilder();
for ( int i = 0; I < lineas.Lenght; i++ )
{
mensaje.Append( lineas[i] );
}
return mensaje.ToString();
}
ASP.NET
- No uses variables de sesión a través del código. Usa variables de sesión solo dentro de las clases y expón métodos para acceder el valor almacenado en las variables de sesión. Una clase puede acceder la sesión usando System.Web.HttpContext.Current.Session
- No almacenes objetos grandes en la sesión. Almacenar objetos grandes en la sesión puede consumir mucha memoria del servidor dependiendo del número de usuarios.
- Siempre usa hojas de estilo para controlar el look and feel de las páginas. Nunca especifiques nombre de las fuentes y tamaño en ninguna de las páginas. Usa clases de estilo apropiadas. Esto asegurará que puedas cambiar la interfaz gráfica de tu aplicación fácilmente en el futuro. También, si deseas soportar la personalización de la interfaz gráfica para cada cliente, es solo cuestión de desarrollar otras hojas de estilo para ellos.
Arquitectura
Siempre utilice arquitectura multicapa (N-Tier).

- Nunca accedas a la base de datos desde las páginas de la interfaz gráfica. Siempre ten un conjunto de clases de capa de acceso a datos la cual ejecute todas las tareas relacionadas con la base de datos.
- Usa las sentencias try-catch en tu capa de acceso a datos para atrapar todas las excepciones de la base de datos. Este controlador de excepciones debe registrar todas las excepciones desde la base de datos. Los detalles registrados deben incluir el nombre del comando siendo ejecutado, nombre del procedimiento almacenado, parámetros, cadena de conexión usada etc. Después de registrar la excepción, debe de re lanzarse para que la otra capa en la aplicación la atrape y tome las acciones apropiadas
- Separa tu aplicación en múltiples ensamblados. Agrupa todas las clases de utilidades independientes (acceso a archivos, validaciones genéricas, literales, constantes…) dentro de una librería de clases separada. Todos tus archivos relacionados a la base de datos pueden ser otra librería de clases.
- Evitar al máximo elementos de “terceros” en el entorno de desarrollo o arquitecturas. Comprometen la escalabilidad futura, pues esos “terceros” no saben cómo Microsoft orientará su próxima estrategia de desarrollo y puede comprometer el proyecto. Por ejemplo, jQuery, por lo que los otros actores (Dojo, MooTools, Prototype…) pueden quedar fuera de juego. Hay que adoptar aquellos frameworks que Microsoft adopte claramente o nos quedaremos cautivos. Y la historia está llena de cadáveres tecnológicos con grandes expectativas, como por ejemplo Adobe Flash, Microsoft apuesta por HTML5 y Silverlight.
- KISS (Keep It Simple, Stupid – Mantenlo simple, estúpido): Se recomienda el desarrollo empleando partes sencillas, comprensibles y con errores de fácil detección y corrección, rechazando lo enrevesado e innecesario en el desarrollo de sistemas complejos en ingeniería. En SCRUM es un pilar imprescindible.
- El código está bien formado cuando alguien que no lo ha codificado es capaz de leerlo sin necesidad de adivinar que hace y que necesita. (ver técnicas de revisión de código: Peer Review, …etc.) Recuerda que: pasado dos meses si tu código “ni tú” sabes lo que hace es que está mal conceptuado y las funciones mal definidas. “Buen código es aquel que hace obvio lo que está pasando”
- . KAIZEN, metodología de calidad en la empresa y en el trabajo, en su vertiente para los desarrolladores IT nos dice: “A los informáticos nos gusta la tecnología, nos imaginamos grandes catedrales de arquitecturas”. Olvidemos las catedrales, recordemos, pues, a KISS.
- Seguir los patrones S.O.L.I.D. lo mejor posible:
- SRP (Single Responsibility Principle):
El principio de responsabilidad única nos indica que debe existir un solo motivo por el cual la clase debe ser modificada, o sea, que la clase debe tener un solo propósito. Es el principio más fácil de violar.
- OCP (Open-Closed Principle):
El principio Abierto/Cerrado indica que las clases deben estar abiertas para la extensión y cerradas para la modificación, o sea, que una clase debe poder ser extendida sin tener que modificar el código de la clase.
- LSP (Liskov Substitution Principle):
El principio de sustitución de Liskov indica que las clases derivadas (hijas) pueden ser sustituidas por sus clases base. Se promueve que la herencia se realice en forma transparente, o sea, no se debe implementar métodos que no existan en sus clases base ya que de lo contrario se rompe el principio.
(De OCP y LSP se deduce que las clases base (abstractas o no) modelan el aspecto general y las clases heredadas modelan el comportamiento local.”)
- ISP (Interface Segregation Principle):
El principio de segregación de interfaces indica que hay que hacer interfaces de grano fino que son específicos de clientes, dicho de otra forma, muchas interfaces muy especializadas son preferibles a una interfaz general en la que se agrupen todas las interfaces.
- DIP (Dependency Inversion Principle):
El principio de inversión de dependencias indica que las abstracciones no deben depender de los detalles, los detalles deben depender de las abstracciones.
- Sé ACID (Atomicity, consistency, isolation, and durability) con los datos.
- Atomicidad: es la propiedad que asegura que la operación se ha realizado o no, y por lo tanto ante un fallo del sistema no puede quedar a medias.
- Consistencia: Integridad. Es la propiedad que asegura que sólo se empieza aquello que se puede acabar. Por lo tanto, se ejecutan aquellas operaciones que no van a romper las reglas y directrices de integridad de la base de datos.
- Aislamiento: es la propiedad que asegura que una operación no puede afectar a otras. Esto asegura que la realización de dos transacciones sobre la misma información sean independientes y no generen ningún tipo de error.
- Durabilidad: es la propiedad que asegura que, una vez realizada la operación, ésta persistirá y no se podrá deshacer aunque falle el sistema.
- Cumpliendo estos 4 requerimientos un sistema gestor de bases de datos puede ser considerado ACID Compliant.
- Web Service. No abusar de ellos. Es un modelo que consume muchos recursos. Hoy, por hoy, no debería usarse de forma directa y debería ser usado tras un WCF, que nos permite hacer uso de TCP/IP mucho más ágil, rápido y reducción de consumo de recursos.
- Unificación de Interface. En un proceso de diseño de interface con usuarios u otros servicios, hay que realizar unas sesiones de común acuerdo en donde va cada elemento y la manera que se interactuará con el usuario. En el caso de interface con usuario, se recomienda seguir algún patrón de ergonomía + la opinión del usuario. Crea un prototipo y muéstralo al usuario, no siempre lo “científicamente” adecuado es lo idóneo.
- Versionado y ciclo de vida de una aplicación.
- Alfas: aplicación en desarrollo, no operativo. Se nombra así: 0.0.0001
- Betas: aplicación concluida en sus funcionalidades principales. De alcance interno y de testeadores funcionales (FDD). Está en equipos de Integración para el resto de desarrolladores y testeadores. Eliminación de errores de forma activa. Se nombra: b1.0
- RC (Release Candidate) Candidata a Versión: Cuando todos los test han sido pasados y solo queda su distribución o implementación (está en pre-producción). De alcance público Se nombra: rc1.0
- RTM (Release To manufacturing), opcional. Se considera el estado del producto definitivo que puede ser usado antes de su distribución o uso masivo.
- Release Final. Producto considerado final y en producción. Se nombra 1.0
- Release menor, de mejora menor o corrección de bug una vez en producción. Se nombra 1.1
Estándares de implementación para la seguridad de la infomración para la suite ID Digipro
Es estrictamente necesario que el área de desarrollo implemente los siguientes estándares de seguridad para la suite de I&DPortal, los cuales serán evaluados por el área de Pruebas.
Control de acceso
- Asegurar que todas las peticiones pasan por un formulario de autenticación, y que éste no se puede saltar.
- Asegurar que todas las páginas, o módulos cumplen el requisito de autenticación.
- Asegurar que siempre que se pasen credenciales de autenticación (o cualquier información sensible), sólo se aceptará la información vía HTTP POST y nunca con GET.
- Asegurar que las credenciales de autenticación no van en claro.
- Asegurar que no hay “puertas traseras” en el código en producción.
Autorización
- Asegurar de no comprometer información sensible.
- Asegurar que no se puedan hacer operaciones no autorizadas manipulando cookies
- Asegurar el uso de cifrado de las cookies con datos sensibles
- Determinar si todas las transiciones de estados en el código de la aplicación verifican el uso seguro de cookies
Logs
- Asegurar que no registramos información sensible en el log en caso de error.
- Asegurarnos de definir y controlar la longitud máxima de una entrada de log.
- Asegurar que no registramos datos sensibles en el log: cookies, método HTTP “GET”, credenciales de autenticación.
- Asegurar el registro en el log las operaciones de autenticación (fallidas o exitosas).
- Asegurar el registro en el log los errores de la aplicación
- Determinar si al hacer debug estamos registrando en el log datos sensibles
Cifrado
- Asegurar que no se transmiten datos sensibles en claro, interna o externamente.
- Se usen métodos de cifrado en base de datos y archivos.
- Examinar en la estructura de ficheros si hay algún componente que no debe estar directamente accesible para los usuarios.
- Comprobar la gestión de memoria (reservar/liberar).
- Comprobar si la aplicación usa SQL dinámico y determinar si es vulnerable a inyecciones de código
- Comprobar si la aplicación tiene funciones “main()” ejecutables y depurar “puertas traseras”.
- Buscar código comentado (aunque sea para pruebas) que pueda contener información sensible
- Asegurar que todas las bifurcaciones de código tengan su cláusula default (ifelse, switchdefault etc.)
- Asegurar que no hay “development environment kits en los directorios en explotación
- Buscar llamadas al sistema operativo, así como aperturas de ficheros, y comprobar las posibilidades de error.
Gestión de sesiones
- Comprobar cómo y cuándo se crean las sesiones de usuario, ya sean autenticadas o no.
- Comprobar el ID de sesión y verificar que tiene la complejidad necesaria para “ser fuerte”.
- Comprobar cómo se almacenan las sesiones: en base de datos, en memoria, etc.
- Comprobar cómo la aplicación hace el seguimiento de las sesiones (track sessions).
- Determinar qué hace la aplicación en caso de encontrar un ID de sesión inválido
- Comprobar la invalidación de sesiones
- Determinar cómo se gestionan las sesiones multithreaded/multiuser
- Determinar el timeout de inactividad de la sesión HTTP
- Determinar cómo funciona el logout.