Llegué a este diagrama obra de Paula Scher saltando desde un artículo de Coding Horror.
miércoles, 8 de julio de 2009
La ¿evolución? de la discusión.
viernes, 12 de junio de 2009
NUTS-QL (Negate/Unite/Test Standard Query Language) [DRAFT].
Nota para lectores del feed: el código SQL es más legible si lo ven formateado en la entrada original.
¿Qué es NUTS-QL? Para hacer honor a su nombre, comencemos por la negativa: NUTS-QL no es un dialecto SQL. NUTS-QL es una técnica de creación y mantenimiento de consultas SQL, un conjunto de prácticas y principios, y por lo tanto puede aplicarse en conjunción con cualquiera de los dialectos de SQL conocidos.
¿Cuáles son sus orígenes? NUTS-QL es una etiqueta bajo la cual se agrupa un conjunto coherente de técnicas que -por separado- son conocidas y utilizadas por innumerables desarrolladores a lo largo y ancho del globo. Esta compilación y su organización es obra, humildemente, de mi autoría.
¿Cuáles son sus ventajas? La principal ventaja de NUTS-QL es que va más allá de asegurar un código SQL legible: hace que la legibilidad sea irrelevante.
Vamos directamente a un ejemplo. El requerimiento será desarrollar el “Reporte de movimientos diarios”.
El primer paso es escribir lo primero que se nos venga a la cabeza, probar que compile y mandarlo a pruebas:
SELECT C.ID, C.DESCRIPCION, D.DEBE, D.HABER FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID WHERE C.FECHA = @FECHA ORDER BY C.FECHA
¡Y eso es todo! No hace falta documentar nada. En el improbable caso de que luego de un par de días, semanas o meses, a alguien le toque modificar la consulta, aplicará los principios de NUTS-QL.
Digamos que ahora se necesita que “en los movimientos de emisión de cheques (MOVIMIENTOS_CABECERA.TIPO=1) se presente el número de cheque emitido”.
Lo primero que tenemos que hacer es implementar el requerimiento con una consulta aparte que satisfaga sólo el caso mencionado. Si bien puede utilizarse la consulta anterior como base, usualmente es más fácil no preocuparse por lo que ya está hecho, que probablemente no sirva para nada. Otra vez escribimos lo primero que se nos viene a la cabeza:
SELECT C.ID, C.DESCRIPCION, CHQ.NUMERO, D.DEBE, D.HABER FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID INNER JOIN CHEQUES AS CHQ ON CHQ.MOVIMIENTO_EMISION_ID = C.ID WHERE C.FECHA = @FECHA AND C.TIPO = 1 ORDER BY C.FECHA
Ahora hay que integrar las dos consultas. Son tres fases. La primera es la de negación (Negative), en la que excluimos de las cláusulas anteriores los registros que incorporamos en la nueva. Esto es fácil porque podemos tomar el WHERE de la nueva y agregarlo en la primera precedido por los operadores AND NOT (ver que la línea 5 del ejemplo siguiente es la negación de la línea 5 del ejemplo anterior). La primera consulta nos queda:
SELECT C.ID, C.DESCRIPCION, D.DEBE, D.HABER FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID WHERE C.FECHA = @FECHA AND NOT (C.FECHA = @FECHA AND C.TIPO = 1) ORDER BY C.FECHA
La segunda es la de unión (Unite), en donde unimos las dos consultas con la cláusula UNION:
SELECT C.ID, C.DESCRIPCION, D.DEBE, D.HABER FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID WHERE C.FECHA = @FECHA AND NOT (C.FECHA = @FECHA AND C.TIPO = 1) ORDER BY C.FECHA UNION SELECT C.ID, C.DESCRIPCION, CHQ.NUMERO, D.DEBE, D.HABER FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID INNER JOIN CHEQUES AS CHQ ON CHQ.MOVIMIENTO_EMISION_ID = C.ID WHERE C.FECHA = @FECHA AND C.TIPO = 1 ORDER BY C.FECHA
La tercera es la de prueba (Test), que consiste en ejecutar y corregir hasta que funcione. En este caso hay dos problemas: el ORDER BY está repetido (va uno sólo al final) y la cantidad de campos en la sección SELECT no coinciden entre las dos partes (rellenamos con nulos donde sea necesario). Necesitaremos al menos dos intentos. El resultado será:
SELECT C.ID, C.DESCRIPCION, NULL AS NUMERO, D.DEBE, D.HABER FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID WHERE C.FECHA = @FECHA AND NOT (C.FECHA = @FECHA AND C.TIPO = 1) UNION SELECT C.ID, C.DESCRIPCION, CHQ.NUMERO, D.DEBE, D.HABER FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID INNER JOIN CHEQUES AS CHQ ON CHQ.MOVIMIENTO_EMISION_ID = C.ID WHERE C.FECHA = @FECHA AND C.TIPO = 1 ORDER BY C.FECHA
Es muy, muy improbable que surja una tercera modificación o error, pero agreguemos una a modo de ejemplo. Supongamos que nos enteramos de que “los importes en moneda extranjera (registros en los que MOVIMIENTOS_DETALLE.IDMONEDA <> NULL) no se están convirtiendo a la moneda corriente de acuerdo al tipo de cambio indicado en MOVIMIENTOS_DETALLE.COTIZACION”.
Apliquemos NUTS-QL. La consulta para los registros en moneda extranjera será:
SELECT C.ID, C.DESCRIPCION, D.DEBE * D.COTIZACION, D.HABER * D.COTIZACION FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID WHERE C.FECHA = @FECHA AND NOT D.IDMONEDA IS NULL ORDER BY C.FECHA
Ahora, en la etapa de negación, debemos modificar la sección WHERE de las consultas anteriores negando la condición WHERE de la nueva sección (ok, son 2, pero es sólo copiar y pegar).
La primera queda:
WHERE C.FECHA = @FECHA AND NOT (C.FECHA = @FECHA AND C.TIPO = 1) AND NOT (C.FECHA = @FECHA AND NOT D.IDMONEDA IS NULL)
y la segunda:
WHERE C.FECHA = @FECHA AND C.TIPO = 1 AND NOT (C.FECHA = @FECHA AND NOT D.IDMONEDA IS NULL)
Luego, en la etapa de unión nos queda:
SELECT C.ID, C.DESCRIPCION, NULL AS NUMERO, D.DEBE, D.HABER FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID WHERE C.FECHA = @FECHA AND NOT (C.FECHA = @FECHA AND C.TIPO = 1) AND NOT (C.FECHA = @FECHA AND NOT D.IDMONEDA IS NULL) UNION SELECT C.ID, C.DESCRIPCION, CHQ.NUMERO, D.DEBE, D.HABER FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID INNER JOIN CHEQUES AS CHQ ON CHQ.MOVIMIENTO_EMISION_ID = C.ID WHERE C.FECHA = @FECHA AND C.TIPO = 1 AND NOT (C.FECHA = @FECHA AND NOT D.IDMONEDA IS NULL) ORDER BY C.FECHA UNION SELECT C.ID, C.DESCRIPCION, D.DEBE * D.COTIZACION, D.HABER * D.COTIZACION FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID WHERE C.FECHA = @FECHA AND NOT D.IDMONEDA IS NULL ORDER BY C.FECHA
Ya en la etapa de test, nos damos cuenta de que (otra vez) el ORDER BY está repetido y de que tenemos que rellenar el SELECT de la nueva consulta con campos nulos para que coincida con el de las anteriores. El resultado final será:
SELECT C.ID, C.DESCRIPCION, NULL AS NUMERO, D.DEBE, D.HABER FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID WHERE C.FECHA = @FECHA AND NOT (C.FECHA = @FECHA AND C.TIPO = 1) AND NOT (C.FECHA = @FECHA AND NOT D.IDMONEDA IS NULL) UNION SELECT C.ID, C.DESCRIPCION, CHQ.NUMERO, D.DEBE, D.HABER FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID INNER JOIN CHEQUES AS CHQ ON CHQ.MOVIMIENTO_EMISION_ID = C.ID WHERE C.FECHA = @FECHA AND C.TIPO = 1 AND NOT (C.FECHA = @FECHA AND NOT D.IDMONEDA IS NULL) UNION SELECT C.ID, C.DESCRIPCION, NULL, D.DEBE * D.COTIZACION, D.HABER * D.COTIZACION FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID WHERE C.FECHA = @FECHA AND NOT D.IDMONEDA IS NULL ORDER BY C.FECHA
Y listo, nuevamente a pruebas y producción.
Los puristas encontrarán que en aquellos registros que corresponden a cheques nominados en moneda extranjera no se está mostrando el número. En el cuasi-imposible caso de que esto se detecte nos llegará el requerimiento correspondiente. Normalmente deberíamos entender toda la consulta y buscar el error. Gracias a NUTS-QL simplemente hacemos lo de siempre.
Para abreviar les dejo sólo el resultado final de aplicar la técnica a este último caso (es un buen ejercicio que el lector lo haga por su cuenta y compare los resultados):
SELECT C.ID, C.DESCRIPCION, NULL AS NUMERO, D.DEBE, D.HABER FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID WHERE C.FECHA = @FECHA AND NOT (C.FECHA = @FECHA AND C.TIPO = 1) AND NOT (C.FECHA = @FECHA AND NOT D.IDMONEDA IS NULL) AND NOT (C.FECHA = @FECHA AND C.TIPO = 1 AND NOT D.IDMONEDA IS NULL) UNION SELECT C.ID, C.DESCRIPCION, CHQ.NUMERO, D.DEBE, D.HABER FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID INNER JOIN CHEQUES AS CHQ ON CHQ.MOVIMIENTO_EMISION_ID = C.ID WHERE C.FECHA = @FECHA AND C.TIPO = 1 AND NOT (C.FECHA = @FECHA AND NOT D.IDMONEDA IS NULL) AND NOT (C.FECHA = @FECHA AND C.TIPO = 1 AND NOT D.IDMONEDA IS NULL) UNION SELECT C.ID, C.DESCRIPCION, NULL, D.DEBE * D.COTIZACION, D.HABER * D.COTIZACION FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID WHERE C.FECHA = @FECHA AND NOT D.IDMONEDA IS NULL AND NOT (C.FECHA = @FECHA AND C.TIPO = 1 AND NOT D.IDMONEDA IS NULL) UNION SELECT C.ID, C.DESCRIPCION, CHQ.NUMERO, D.DEBE * D.COTIZACION, D.HABER * D.COTIZACION FROM MOVIMIENTOS_CABECERA AS C INNER JOIN MOVIMIENTOS_DETALLE AS D ON C.ID=D.ID INNER JOIN CHEQUES AS CHQ ON CHQ.MOVIMIENTO_EMISION_ID = C.ID WHERE C.FECHA = @FECHA AND C.TIPO = 1 AND NOT D.IDMONEDA IS NULL ORDER BY C.FECHA
Comentarios: se le achaca a este método producir sentencias cada vez más ilegibles e ineficientes. Pero hemos visto que no es necesario leerlas, por lo que el primer punto es irrelevante. En cuanto al segundo… bueno, eso es un problema de hardware ¿no?
Lo mejor de todo es que lograr la adopción de NUTS-QL es fácil: una vez que un desarrollador comienza los demás están obligados a seguirlo, basta con respetar el punto de no documentación de requerimientos (si éstos estuviesen documentados, un programador inexperto podría estar tentado a reescribir toda la sentencia). Así, es un camino de ida.
Nota para lectores del feed: el código SQL es más legible si lo ven formateado en la entrada original.
jueves, 4 de junio de 2009
Una mala costumbre.
Identificado 100% con la última tira de Sinergia sin control. No hay nada que hacer: los programadores cometemos errores, probamos mal, nos vamos por las ramas, ignoramos las reglas más básicas del negocio… y los PM hacen este tipo de cosas (del tipo que ilustra la imagen de aquí al lado)… todo el tiempo y en todos lados. Aunque mal de muchos consuelo de tontos… por lo menos entiendo que no es algo personal.
Tal vez peque de corporativismo… no puedo dejar de pensar que hay una diferencia sustancial. Los programadores no podemos dejar de cometer errores, sin importar cuánto lo intentemos… cualquiera que se de un paseo por allí verá que (en general) de todas maneras hacemos un gran esfuerzo por evitarlos. ¿Es igualmente imposible no comprometer el tiempo de los demás sin consultarles? ¿No es de sentido común?
Se ve que no.
jueves, 4 de septiembre de 2008
Google shit.
No estoy de acuerdo con eso de andar comentando cualquier cosa de cualquier manera, y sobre todo utilizando malas palabras y groserías cuando no corresponde, y no me gusta hacerles la fiesta a los que lo hacen... pero hay veces en que alguno de estos comentarios inapropiados es demasiado divertido como para aguantar la risa.
Axel Marazzi postea en Alt1040 el artículo "Los abouts de Google Chrome", comenzando con la siguiente frase:
"Muchos de nosotros ya adoptamos a Google Chrome como explorador por defecto. De hecho ya me comencé a preguntar qué era exactamente Firefox, no lo recuerdo bien. Todas las personas que estén pasando por la misma situación que yo tienen que tener en cuenta los siguientes abouts que te mostrarán el rendimiento del programa en tu ordenador."
Ok, al tipo le gustó el Chrome, eso está claro. El usuario uno (anónimo tenía que ser, por supuesto) le comenta:
[...] el día que Google venda mierdas te volverás necrófago (en realidad es coprófago) y podremos ver una entrada del estilo: Ya sólo como Google shit y ni me acuerdo de qué es eso de la carne ni el pescado [...]
lol
Ya sé que no debería hacerle la fiesta a este tipo de navegantes, en general no lo hago... pero todavía me sigo riendo, estuvo bien.
lunes, 1 de septiembre de 2008
Troll-Post III: "Yo hago desarrollo web"
Jóvenes la mayoría, aunque puede contarse entre sus filas a algún que otro veterano que se cansó de robar con Cobol y descubrió que no es tan difícil desarrollar... web.
Vamos... ¿no les da un poco de calor cobrar por ese flash con cositas recolectadas de aquí y allá, incrustado en un template robado (o en el mejor de los casos copiado) de alguna página web o bajado de la mula?
¿No se les mueve aunque sea un poquito esa cara de piedra que supieron conseguir cada vez que facturan "plataforma de blog" mientras esperan que baje la última versión del WordPress?
¿O cuando le ponen "mantenimiento de hosting"? Vamos... que le paso el cliente a mi amigo ZZZ Hosting que me hace un descuento, multiplico por 1.7 (¿o mejor por 2.7?) y ahí tenemos el precio.
Pero imagino que el mayor logro es cobrar por un dominio ".com.ar" (que lo he visto)... ¿y cómo no vamos a cobrarlo si lo tenemos registrado a nuestro nombre? Ese mail que hay que mandar todos los años no sale sólo.
Si el diseño te lo da el diseñador, el contenido el cliente, el script para mandar los mails lo buscaste en un foro y el dominio es gratis... ¿con qué no robás? Ah, con el SEO... ésa sí que es buena, cobrar por lo que de todas maneras Google va a terminar haciendo... gratis. No, pero no todo es Google, hay un millón de otros sitios en los que hay que dar de alta al cliente, aunque no sirva para nada.
Un poco de este libro de diseño, otro poco de ¿pseudocódigo?php, mucho de foro y preguntar y ya está, nos recibimos de desarrolladores web.
No entiendo cómo puede desarrollarse un sistema que no hace nada... Bueno sí, manda mails, supongo que en una de esas también tiene una base de datos de contactos, un feed, una página con estadísticas (que nos dan en el hosting en el mejor de los casos, sino directamente Google Analytics, ¿por qué hacer lo que ya está hecho?)...
En fin... no se preocupen, son sólo envidias de uno que se tiene que levantar a trabajar en serio todas las mañanas... nos vemos.
P.D.: si ésta no te tocó, tal vez sí Troll-Post II: ¿Es alguna variante de Basic un lenguaje de programación? o Troll-Post I: Programación de bajo nivel.
martes, 5 de agosto de 2008
Troll Post II
¿Es alguna variante de Basic un lenguaje de programación?
Es decir sí, claro que lo es, formalmente hablando. Digo, puede hacer que una computadora haga algunas cosas (con mucha garra)... ¿pero puede considerarse un lenguaje de programación profesional?
Que no se me malinterprete, yo a Basic lo quiero mucho, empecé con él... pero justamente a eso me refiero: para aprender a programar utilicé GW Basic, luego aprendí un poco más y utilicé QBasic, luego un poco más y comencé con VBA (ya en la era Windows), luego ASP 3.0 (que es casi el mismo jueguito), y luego VB 6, luego VB .NET... y luego C#.
¿Ven lo que digo? A medida que fui progresando fui cambiando de versión de Basic, cada vez más alejada de la original (cada vez más parecida a lenguajes profesionales), hasta que completé mis estudios de programador, me convertí en un Arquitecto de Software que programa en serio y comencé a usar C#.
Claro que es un buen lenguaje para aprender, tanto programación como inglés mismo, ya que es casi la misma sintaxis. Para los chicos más chicos han salido incluso versiones en castellano (en VBA del Office 5.0) que evitan que uno se confunda al tener que utilizar, por ejemplo, inglés para las instrucciones y castellano para las variables... ¡es difícil, yo lo sé!
Las intrucciones son descriptivas, las palabras clave simpáticas ("Friend", "Me", "MyBase") y el lenguaje mismo nos ayuda con nuestros errores más comunes, como por ejemplo los de conversiones entre tipos de datos. No hace falta saber qué es un puntero, qué es una referencia ni demasiado de programación orientada a objetos ni namespaces ni nada de eso.
Existen, en resumen, infinidad de ventajas para los novatos.
Si ud., querido lector, todavía utiliza profesionalmente algún derivado de este lenguaje, es simplemente porque todavía no ha alcanzado la madurez profesional. Pero no sea tonto, no se sienta menos que todos hemos transitado la ruta del aprendizaje. No cometa la imprudencia de adelantarse, de apurarse. El aprendizaje finalizará en algún momento, y ud. abandonará naturalmente la "crisálida Basic".
Si ud. es jefe de proyecto o dueño de una consultora o empresa similar, tampoco se preocupe si descubre ahora que su equipo estrella es un puñado de alumnos de programación, igual pueden hacer que las cosas funcionen. Protéjalos del mundo y espere pacientemente. En algún momento vendrán con el pedido de cambiar de lenguaje. De todas maneras, si aparece algún proyecto de mayor envergadura todavía puede contratar uno o dos programadores verdaderos que los apoyen, y de esta manera aprenderán más rápido todavía.
¡Saludos a todos los aprendices! Sigan leyendo y ¡hasta la próxima!
miércoles, 30 de julio de 2008
Troll-Post
A ver, acabo de bajar este post de h@anz...el Geek que recuerda la famosa frase
La programación en bajo nivel es buena para el alma del programador.
esta vez adjudicada a John Carkmack (digo adjudicada porque ya he leído tantas variaciones por tantos personajes diferentes que no pongo las manos en el fuego por ningún crédito).
Más allá de lo gracioso de la frase en sí, yo me pregunto... ¿a quién miércoles puede gustarle programar en bajo nivel? Solo una manga de inadaptados ajenos a la realidad pueden preocuparse por si el bit n está en 1 o en 0 y festejar si logran hacer una aplicación que, por ejemplo... ¡comunique dos dispositivos por un puerto serie! (algo que para un programador "como la gente" -de "alto" nivel- sería casi trivial)...
¿Cómo es que puede existir, en pleno siglo XXI, gente deseosa de lidiar con MOV PUSH CLR SET y yo qué se qué más (¿qué problema tienen contra las instrucciones de más 4 letras?)...
Parecen fanáticos ingleses o yanquis, a contramano del mundo, utilizando el sistema ¡hexadecimal! u ¡octal! (¿cómo es que eligen esas bases? Supongo que se juntan todos en convención y empiezan a discutir "yo creo que base 9 es la más difícil de entender, usemos esa"... "no, no, no, base 17 es peor... mejor quiero decir...", "yo tengo otra idea... usemos una base aleatoria...")... pero ¡¡¡por Dios!!!! ¿CUANTOS DEDOS TIENE ESTA GENTE? ¿2, 4, 8, 16, 32... CUÁNTOS?
En fin, por suerte viven encerrados en sus cajitas de silicio, gracias al cielo por ello.
PD: Ahora en serio, saludos a todos uds. con el mayor de los cariños y la más grande de las envidias... pero no me vengan con sus problemas, si se puede tocar no tiene nada que ver conmigo.