Malas Prácticas: Documentos Funcionales Ligados a la Tecnologí­a

Hasta el momento solo habí­amos hablado de malas y buenas prácticas que estaban asociadas a labores de construcción de bajo nivel, pero, éstas, aunque siendo sin duda malas prácticas, quedan bajo la sombra que provocan otras prácticas de dudosa viabilidad que se llevan a cabo en niveles más altos, y sobre las que el impacto en un proyecto de desarrollo de software es aíºn mucho más pesado.
Un ejemplo muy claro es el incluir en un documento de descripción de la funcionalidad (Documento funcional o de Requerimientos o como estéis más acostumbrado a llamarlo), descripciones que tienen que ver con la parte técnica. Si además de describir cómo ha de comportarse algo funcionalmente, incluimos la pantalla asociada a la funcionalidad en el mismo documento, y además describimos lo que tiene que hacer la pantalla (si pulso aquí­ se desplegará un meníº, si hago botón derecho me saldrán las opciones “juan” y “pedro”, si paso el ratón por encima de la zona central se mostrará un aviso indicando el nombre del formulario…) estamos haciendo el documento funcional totalmente dependiente de la tecnologí­a a emplear, cuando este íºltimo deberí­a limitarse íºnicamente a describir el negocio de la aplicación.
De ahí­ que, bajo mi punto de vista, así­ como el uso de diferentes capas a la hora de definir una arquitectura de software es una buena herramienta de diseño, aplicar el mismo concepto a la documentación funcional también lo es.
Serí­a interesante entonces trabajar a dos niveles. Inicialmente crear el documento estrictamente funcional, desarrollado por un analista o experto en el negocio  incluyendo en el mismo casos de uso, requerimientos y reglas del negocio. A partir de ahí­, y tras análisis del documento funcional, un perfil algo más técnico, con conocimientos de usabilidad y de arquitectura de software, deberí­a evaluar la documentación y proponer (además de una arquitectura de software adecuada para el tipo de funcionalidad requerida), cómo orientar la funcionalidad a la pantalla que va a ver el usuario, creando los prototipos pertinentes, bajo la perspectiva de la tecnologí­a seleccionada para la solución.
Saludos.
Miguel.

Mala Definición de la Estructura de un INSERT

Esta sentencia SQL es correcta sintácticamente y por tanto ejecutable:
INSERT INTO
COCHE
VALUES
(1, ‘Seat’, ‘IB-4345-CK’, ’12/06/2008′)
Pero tiene un grave problema, no estamos indicando cual es el campo de la tabla donde se va a insertar la información, por lo que por defecto, el sistema gestor de bases de datos pertinente lo asignará a los campos siguiendo el orden en el que fueron creados en la instrucción CREATE TABLE que en su momento se utilizó para dar de alta la tabla en la base de datos.
¿Qué pasa si hago un ALTER sobre la tabla y añado un campo más? Que el INSERT deja de funcionar.
Además, este mal uso es un arma de doble filo porque inicialmente todo funcionará bien, pero de repente, sin saber cómo ni por qué y por “arte de magia” tendremos errores o incosistencias al dar de alta un nuevo coche en nuestro sistema, ya que el orden de los campos habrá cambiado. Si coincide que el campo al que asignamos es del mismo tipo podremos provocar una incosistencia (meter matrí­culas en el campo donde guardamos la marca del coche), y si no coincide, la base de datos lanzará un error.
Por lo tanto, siempre deberí­as utilizar la siguiente sintaxis SQL para un INSERT, en lugar de la que especificábamos anteriormente:
INSERT INTO
COCHE
(CO_CODIGO, MARCA, MATRICULA, FECHA_ALTA)
VALUES
(1, ‘Seat’, ‘IB-4345-CK’, ’12/06/2008′)
Saludos.
Miguel.

Mal uso de las Transacciones

Una transacción permite que al realizar una acción determinada, el estado de la entidad o entidades a las que estemos atacando se mantenga como al principio en el caso de que se produzca algíºn tipo de error en la transacción.
Un tí­pico ejemplo de transacción es la que realizamos contra un cajero automático.
1) Introducimos cantidad a retirar
2) Introducimos nuestra identificación
3) Se resta la cantidad a retirar de la cuenta corriente
4) El cajero extrae los billetes para que los cojamos
Si tras el paso 3 ocurre un error en la aplicación y todos estos puntos no se llevan a cabo de manera transaccional verí­amos como en nuestra cuenta se ha restado el efectivo, pero nunca recibirí­amos los billetes fí­sicamente. En cambio, en un ámbito transaccional todo volverí­a a su estado inicial, quedando el efectivo de la cuenta intacto.
Hasta aquí­ nos ubicamos con el concepto de transacción y ahora vienen las malas prácticas al respecto.
1) La primera y obvia es no utilizar transacciones cuando trabajamos en un contexto como el que hemos citado con el ejemplo. Los datos se perderán o quedarán inconsistentes.
2) Es un error realizar una transacción cuando íºnicamente realizamos consultas a la base de datos. No hay cambios en la misma, no tiene sentido volver a un estado anterior porque nada a cambiado.
3) No tiene sentido utilizar una transacción si se realiza una consulta y una modificación (inserción, actualización, borrado…). Si el error se produce en la modificación no va a pasar nada porque la primer consulta no cambió nada de la base de datos (aunque aquí­ incluyo un matiz que podréis ver en los comentarios de ese post, que podrí­an darse casos donde por el contexto de vuestro aplico necesitéis bloquear la tabla con una transacción para que las modificaciones que realicéis después de vuestra consulta no se vean afectadas por otras llamadas concurrentes a las tablas con las que trabajais).
Saludos.
Miguel.

Campos NULL y NOT NULL en la Base de Datos

El tema que voy a desarrollar no es una mala práctica concreta, si no un conjunto de malos usos que tienen que ver con la misma fuente, la mala definición de los tipos de datos almacenados en la base de datos, en concreto en este caso con la reestricción de determinar si un dato puede ser un nulo en un registro.
El uso de las claíºsulas NULL y NOT NULL a la hora de definir un campo de la base de datos tiene una misión muy concreta, simple y a la vez fundamental en la definición de un modelo relacional: Definir si un determinado valor puede permanecer con un estado nulo en un determinado registro.
Esto resulta obvio en la mayorí­a de los casos, por ejemplo, la clave primaria de una tabla es siempre por defecto NOT NULL, no tiene sentido que el valor que distingue de forma íºnica un registro de la tabla esté a nulo.
Una clave foránea no tiene por qué ser NOT NULL.
En otros casos resulta también obvio, aunque el error humano puede provocar que no se establece el NOT NULL y permitir que tablas del tipo Codigo – Descripción tengan el campo Descripción a NULL. Esto suele provocar efectos muy incómodos a la vista, como el tí­pico mantenimiento que al listar los resultados nos aparece la descripción del registro vací­a.
Otro caso que debemos tener en cuenta es el uso de campos NULL en los tipos de dato numéricos. Por ejemplo, si tenemos una tabla con un registro que almacena un temporizador no tenga sentido sea nulo, aunque funcionalmente esté descrito que el valor pueda no estar establecido en un momento dado. Tal vez serí­a mejor la combinación NOT NULL con un valor numérico por defecto, por ejemplo cero. Con lo cual nuestro temporizador partirí­a siempre del valor 0, evitando así­ por ejemplo problemas en tiempo de ejecución en nuestra aplicación (NULL * 3 = ¿?).
Saludos.
Miguel.

Malas Prácticas: Obtener la secuencia de una Clave Primaria con un Select Max()…

Hace no demasiado inaguré la sección de Buenas Prácticas, sección en la que ya se han añadido algunas entradas con recomendaciones al desarrollador.
Hoy dándole vueltas a una nueva buena práctica para añadir (y después de haber oí­do una barbaridad por la oficina), he pensado que tal vez serí­a interesante hablar también de las cosas que no hay que hacer bajo ningíºn caso, de ahí­ surge esta nueva sección llamada “Malas Prácticas”.
Como primera mala práctica vamos a hablar de una forma que se utiliza para obtener la secuencia de la primary key de una tabla. Insisto es una mala práctica, la cual no recomiendo para nada usar, más adelante se explicará por qué. 
Como siempre, lo describo con un ejemplo, que es la forma en la que creo yo que todo queda más claro.
Pongamos el caso de que tenemos una tabla llamada COCHE que consta de dos campos, COCH_CODIGO Y COCH_NOMBRE. COCH_CODIGO es la clave primaria de la tabla, y es de tipo numérico. COCH_NOMBRE es un texto de tamaño 50 caracteres. 
Un requerimiento de nuestra aplicación exige que al dar de alta un nuevo coche en nuestra base de datos, asociemos el código del coche al usuario que ha insertado el coche (imaginemos que la tabla USUARIO contiene un campo llamado COCH_CODIGO donde se guarda la referencia al coche del usuario).
Una MALA PRíCTICA es intentar resolver este problema de la siguiente manera (utilizo pseudocódigo)
/* Obtenemos el código del usuario */
codigoUsuario = obtieneCodigoUsuarioActual();
/* Obtenemos la Id del íºltimo coche que añadimos */
int ultimaId = devuelvePrimerRegistro(“SELECT MAX(COCH_CODIGO) +1 FROM COCHE”);
/* Insertamos el registro */
ejecutarSQL(INSERT INTO COCHE (COCH_CODIGO, COCH_NOMBRE) VALUES (ultimaId, ‘Coche Fantástico’));
/* Actualizamos los datos del usuario */
ejecutarSQL(UPDATE USUARIO SET COCH_CODIGO = ultimaId where USUA_CODIGO = codigoUsuario);
¿Dónde está aquí­ el problema? …
¿Que ocurrirí­a si dos usuarios al mismo tiempo fueran a dar de alta un nuevo coche? Fijaros en la secuencia
1) El primero harí­a el SELECT MAX y obtendrí­a el código 23.
2) Antes de que el primero haga el INSERT en la tabla COCHE, el segundo harí­a el SELECT MAX y obtendrí­a también 23
3) El primero hacer el INSERT, todo va bien.
4) El segundo hacer el INSERT… y PUM, CONSTRAINT ERROR, Clave Primaria duplicada.
¿Soluciones? Afortunadamente existen unas cuantas.
1) Secuencias ORACLE
Si usáis como base de datos ORACLE estáis de suerte, ya que aquí­ existe el concepto de secuencia. Puedes crearte una secuencia para cada tabla, y antes de realizar el insert pedir el siguiente níºmero de dicha secuencia.
int secuencia = devuelveEscalar(“SELECT SEQ_COCHE.NEXTVAL”);
ejecutarSQL(INSERT INTO COCHE (COCH_CODIGO, COCH_NOMBRE) VALUES (secuencia, ‘Coche Fantástico’));
ejecutarSQL(UPDATE USUARIO SET COCH_CODIGO = secuencia where USUA_CODIGO = codigoUsuario);
Cuando el segundo usuario que ataque al código concurrentemente pida la secuencia, nunca será la que ha tenido el primero, por lo que nunca saltará la constraint de la foreign key.
2) Secuencias a media
Si trabajáis por ejemplo en SQL Server no tenéis secuencias. Pero eso no quiere decir que no podáis currároslas vosotros. Por ejemplo, podrí­ais crear una tabla llamada SECUENCIA, donde tengas campos por cada una de las tablas de vuestra base de datos. Los campos serí­an de tipo entero.
Os creais una función que le paséis el nombre de la tabla, y que os busque en la tabla SECUENCIA el campo correspondiente a esa tabla, os lo retorne y actualice el campo en la tabla SECUENCIA.
Realmente con esta forma de trabajar podrí­ais hacer igual que con Oracle, pero currado por vosotros.
3) Procedimientos Almacenados
Los procedimientos almacenados os proveen de herramientas para devolveros los datos producidos en un INSERT. Por ejemplo en PL/SQL tenéis la instruccion RETURNING, y en Transact/SQL tenéis el comando @@IDENTITY que te devuelve la íºltima identidad obtenida para la conexión activa.
Saludos a todos.
Miguel