Ya sé, es XKCD, lo lee todo el mundo. Pero me siento tan identificado con esto que tengo que postearlo.
No es algo muy político de decir… pero bueno, estamos entre amigos. La conversación –teorizo- se establece en el mínimo común denominador de los participantes más extrovertidos, lo que hace que a veces me ponga a programar mentalmente. Recalco, a veces (otras estoy sinceramente cansado).martes, 30 de junio de 2009
lunes, 29 de junio de 2009
Lleva a tus monitos de paseo.
La semana pasada escribía sobre el paradigma que rodea cualquier actividad, sobre lo importante que resulta el conocimiento y la comprensión de éste para desarrollar con éxito un software alineado con el negocio, y lo difícil de su transmisión al equipo de desarrollo.
Rara vez es explicitado en manera alguna –decía- y cuando lo es –cuando pretende serlo- se materializa en forma de frases de compromiso que rara vez tienen que ver con la cosa real que dicen reflejar. Es un poco como la visión a la que tanta importancia le dan las normas de calidad: pocos saben qué es y de poco sirve saberlo -salvo para certificarse en algo- pero no se puede tener éxito sin ella. Análogamente, los negocios exitosos no son los que saben qué es un paradigma sino los que tienen un paradigma exitoso. Siempre es bueno racionalizar, poder poner en palabras, explicitar, pero lo indispensable es tener qué explicitar.
El problema es, volviendo al primer párrafo, que el equipo de desarrollo necesita conocer el paradigma real que subyace al modelo de negocio para derivar de él lineamientos concretos con los que diseñar un software. Imaginemos por un segundo que creamos un sistema de gestión para una empresa de la industria farmacéutica partiendo de que, tal cual rezan sus documentos sobre políticas internas, “la base de nuestro negocio es proveer los medicamentos para curar todos los males de la humanidad”… Imaginemos ahora la cara de los directivos cuando mostremos nuestros reportes y panel de control de gestión conformados por indicadores tales como tasa de mortandad, calidad de vida, reducción de casos de tal y tal enfermedad… cuando midamos el éxito de un producto de acuerdo a la cantidad de pacientes que se han curado gracias a él… “¡Hey! ¿Dónde está el estado financiero? ¿Y la cotización en bolsa? ¿Dónde se muestra el ROI?”.
Entonces, para conocerlo y comprenderlo cabalmente se requiere estar ahí. Es tarea propia de analistas funcionales, representantes comerciales y demás. Pero, normalmente, los programadores no pueden estar ahí y la solución propuesta por metodologías ágiles como XP –tener al usuario dentro del equipo- no siempre rara vez es posible. Surge entonces, como siempre en el desarrollo de software, el problema de la comunicación.
Pero… ¿No pueden estar ahí? ¿Nunca? ¿Ninguno? ¿Ni de visita? ¿Ni por un rato? Difícil de creer. A pocos se les da la oportunidad de conocer a los usuarios en su entorno real de trabajo. Incluso cuando esto es posible, incluso cuando es sencillo encontrar la oportunidad. Más raro aún es que esta actividad sea establecida formalmente entre las tareas de un programador durante el proceso de desarrollo.
Para los que han tenido esta experiencia -como parte de una visita formal o no- ha sido casi reveladora. Día a día, inevitablemente y de forma intuitiva, encerrados tras una muralla de código, los programadores derivan, imaginan, se forman una imagen del negocio para el cual programan. Lo hacen a partir de los requerimientos, del diseño propuesto para las pantallas, de las funcionalidades. Pero es muy difícil que esta imagen, condicionada por su experiencia y sus propios paradigmas, coincida con la realidad.
Creo que es una práctica fácil, barata y muy productiva el llevar a los programadores “de paseo” al entorno del cliente en cualquier oportunidad que se presente (oportunidad para tomarles fotos vestidos decentemente… ¡o hasta de saco y corbata!). ¿Para qué? Para que conozca a los usuarios, su nivel, su entorno, su trabajo. Por ejemplo…
¿Son usuarios avanzados en el uso de una computadora en general? Hace ya mucho tiempo (no creo que esto pueda suceder ahora) me di cuenta de que el significado de los íconos más básicos y estándar –guardar, abrir- de la barra de tareas del sistema eran completamente oscuros para el usuario de nuestro sistema, que utilizaba sólo el menú porque allí decía “Abrir”, “Guardar”, haciendo todo de una manera muy incómoda. El detalle cool (para mí) de poner íconos grandes y sin texto hacía que la barra fuese completamente inútil. El gesto –tan natural para mí- de poner el cursor encima de un ícono para ver una descripción de su funcionalidad era imposible en esta persona.
¿Son jóvenes, personas mayores? ¿Necesitan algún tipo de accesibilidad para utilizar el sistema? Allá lejos y hace tiempo tuve un compañero de trabajo, un contador –creo- que era tan corto de vista que tenía que usar la lupa… y les aseguro que el sistema se veía MUY diferente de esa manera.
¿Su trabajo les exige interacción constante con el sistemao es más bien esporádica? ¿Tienen otros sistemas abiertos? ¿Tienen que consultarlos alternativamente? ¿Llevan a cabo varias tareas al mismo tiempo? Esa transacción, que nosotros imaginamos como una rápida carga de datos y un enter final tal vez requiere de varios minutos entre ingreso e ingreso para conseguirlos. Otra de las cosas impensadas que vi fue un usuario que primero copiaba todos los datos necesarios a un archivo de texto. Lo hacía desde diferentes planillas, sistemas, desde el navegador y luego abría el sistema de presupuestos y los pegaba desde allí. ¿Por qué? Era obvio, porque temía perderlos y el sistema no le dejaba grabar el registro –aunque sea localmente o en un repositorio temporal- si no tenía los datos completos. Esta era una funcionalidad que yo había discutido tercamente por demasiado problemática en relación al beneficio –según mi propio punto de vista- que representaba para el usuario. Cuando lo vi no discutí más.
¿Están cómodos? No es lo mismo utilizar el sistema localmente que hacerlo a través de un escritorio remoto, o sentado cómodamente en un escritorio que de parado frente a un rack de servidores.
Siempre hay, en definitiva, alguna cosa rara. Se me dirá que es cuestión de hacer lo que los analistas o diseñadores nos piden y listo, que al fin y al cabo ellos han estado allí –o ha leído un documento redactado por alguien que ha hablado con alguien que estuvo allí-, pero eso nos limita a lo que ellos consideran fácil o posible.
Esto último me hizo acordar algo que había escrito en La “respuesta” a “¿Cuál es el error?”, la ambigüedad, su resolución y, finalmente, la realidad:
[…] ya lo dijimos, vivimos en un mundo imperfecto. Muchas veces (en todo proyecto real esta situación es más que frecuente) el cliente no está disponible […] o no entiende la pregunta. ¿Entonces?
[…] hay veces que hay que adivinar y seguir adelante, rellenar los huecos en las especificaciones apelando al sentido común […]
Cultivar el “sentido común” -al que apelan los programadores cuando tienen que inventar algo para tapar algún hueco- mostrándoles “la vida real” del proyecto para el cual trabajan ahorraría innumerables idas y vueltas entre desarrollo y producción y seguramente haría que surjan ideas y mejores posibilidades para el sistema.
domingo, 28 de junio de 2009
sábado, 27 de junio de 2009
viernes, 26 de junio de 2009
Jueguitos de viernes: Planarity.
Planarity es (ya un viejo) juego de ingenio que vi la semana pasada en el blog de juegos de Microsiervos, donde me encontrarán por los comentarios vanagloriándome de mis logros (las imágenes de este post son del nivel 30) y terminando (por ahora) honrosamente derrotado (aquí Lemm resolviendo el 31). | |
El objetivo es simple: disponer los círculos de tal forma de evitar que las líneas que los conectan se crucen. Midan el tiempo perdido por las imágenes que acompañan este post (mas aquí)… mejor no volver a pensar en ello, estoy bastante ocupado y tratando de no jugarlo. | |
Para más fanatismo pueden buscar en YouTube algunas técnicas bastante más estéticas y eficientes que la mía, que puede resumirse como “1% prueba, 1% error y 99% paciencia”. |
Actualización: ya sé, ya sé: 1% + 1% + 99% = 101%... estoy limado.
Typing is not the bottleneck.
…es una frase que resume bastante bien mi visión del desarrollo de software. La encontré en Sebastian Hermida's Blog, quien se sintió inspirado por ella y creó unos stickers -que ofrece a quienquiera pasar el mensaje- con la imagen que ilustra este post.
Si siguen hacia la entrada original descubrirán de dónde viene y un par de links interesantes.
jueves, 25 de junio de 2009
Chuck Norris programador.
Si, ya sé… “yet another list of Chuck Norris jokes”… pero ésta es realmente buena y viene a cuento, ya que se refieren exclusivamente a la actividad del semidios en el campo de la informática.
- Project managers never ask Chuck Norris for estimations…ever.
- All arrays Chuck Norris declares are of infinite size, because Chuck Norris knows no bounds.
- Chuck Norris can’t test for equality because he has no equal.
- The only pattern Chuck Norris knows is God Object.
- Chuck Norris’s keyboard doesn’t have a Ctrl key because nothing controls Chuck Norris.
- When Chuck Norris is web surfing websites get the message “Warning: Internet Explorer has deemed this user to be malicious or dangerous. Proceed?”.
Si fuera por poner las mejores terminaría poniéndolas a todos (son 25 en total), pero esto es apenas una muestra para que sigan hacia {codesqueeze}, The Ultimate Top 25 Chuck Norris “The Programmer” Jokes.
miércoles, 24 de junio de 2009
Impresoras.
Periférico molesto y caprichoso por excelencia, siempre demandando nuestra atención. Que la tinta, que el papel, que los márgenes…
…pero finalmente sufre las consecuencias de su mala actitud. Toda una lección de vida.
Visto en MikeM's Site.
martes, 23 de junio de 2009
Paradigmas.
Los desarrolladores somos –¿hipótesis?- ante todo humanos, luego usuarios, luego desarrolladores.
Como humanos –entre otras cosas-, tendemos a sobrevalorar la propia experiencia. Por ejemplo, si el sistema que desarrollamos nos es fácil de utilizar solemos atribuirle esta característica obviando el hecho de que hemos aprendido a utilizarlo “desde atrás” (es decir, desde el código, el diseño o los requerimientos) y no “desde adelante” (desde la pantalla, los manuales o la capacitación). Para cuando nos enfrentamos a la pantalla de inicio ya sabemos casi de memoria todo lo que sigue. Abstraerse de este conocimiento es –por más que me juren y recontrajuren- imposible, y es por eso que nuestras pruebas y experiencias personales son bastante objetables como medida de lo que sucederá en el mundo real. Esto se aplica, con variaciones, tanto a desarrolladores como a analistas, testers, líderes de proyecto y a cualquiera implicado en el desarrollo de software (excepto, claro está, al cliente y al usuario).
Además de humanos –si es que damos por válida la hipótesis-, somos usuarios de sistemas de un determinado negocio: entornos de desarrollo, clientes de bases de datos, herramientas de diseño y documentación de requisitos, entre muchos otros, conforman el software de soporte al negocio del desarrollo de software. Negocio en el que estamos inmersos y que, como todos, tiene su paradigma establecido y materializado en la forma de determinados estándares: flujos de trabajo, herramientas visuales, disposición física de los objetos en una oficina… todo lo que nos rodea se deriva de éste en alguna medida.
La importancia de estos elementos –paradigma y estándares- que sobre el papel parecen un conjunto arbitrario de caprichos funcionales -¿por qué Ctrl+U y Ctrl+Shift+U? ¿Por qué un árbol y no una serie de listas encadenadas?- radica en que condicionan toda nuestra visión de la realidad (¡vaya si estamos condicionados por los paradigmas de nuestro negocio!). Los sistemas -si pretenden ser exitosos- deberán respetar –o revolucionar, pero esto es más difícil-, sobre todo en la interacción con sus usuarios, los paradigmas del negocio para el que fueron diseñados.
Como desarrolladores que somos –de eso sí estamos seguros- nos sería imposible, si estuviésemos librados a nuestro criterio, no aplicar el paradigma y los estándares propios del negocio al que pertenecemos –el desarrollo de software- a los sistemas que desarrollamos.
El problema es que, como establecimos más arriba, otros negocios o actividades tienen estándares y paradigmas diferentes. Internalizarlos es prácticamente imposible (deberíamos ser, por ejemplo, tan usuarios de distintos sistemas de gestión como lo somos de distintos entornos de desarrollo, tanto como lo es un empleado administrativo que ha transitado por diferentes empresas y puestos). Sólo podemos pretender, a lo más, conocerlos. Y para ello se requiere de una experiencia… “presencial”. Es la experiencia que trata de transmitir el analista funcional, el área comercial o quienquiera que tenga contacto directo con el cliente.
Pero un modelo mental es algo realmente difícil de describir, los estándares que se deriven de éste serán en su gran mayoría reglas y prácticas comunes y no estarán explicitadas en ningún lado. Es, en definitiva, difícil de transmitir. Por todo esto, cuantos más intermediarios haya entre aquellos que conocen a los clientes y usuarios y los desarrolladores, más probabilidades habrá de que nos apartemos del paradigma y los estándares a los que ellos están acostumbrados.
viernes, 19 de junio de 2009
Charlando con Wolfram.
Empecé con una pequeña introducción de rigor…
Are you Skynet? (esto me tenía preocupado)
Is Wolfram|Alpha better than Google?
Decidí desafiarlo. Can you pass the Turing Test?
Ok, ahí vamos… Is there a God?
Why did the chicken cross the road?
How many roads must a man walk down before you can call him a man?
Mejor que algunas personas que conozco. Se ve que sabe de películas, por lo menos:
What’s the speed of an unladen swallow?
¿Y de programación? Hello, World
Bueno, hora de preguntar algo realmente importante. Can you help me?
Es una buena respuesta, pero hay formas más fáciles.
martes, 16 de junio de 2009
Mostrar entradas de hace un año en Blogger.
Mostrar las entradas del mismo día del año pasado en la barra lateral del blog parecía una tarea sencilla, y como toda tarea sencilla se volvió un verdadero desafío. Finalmente he llegado a una solución que, aunque con sus contras, funciona. Por eso les recomendaría leer el post hasta el final antes de decidirse a implementarla.
La solución (más bien el hack) utiliza la Javascript Client Library de Google para obtener los títulos y links de las entradas de hace un año mediante mediante la API de datos de Blogger.
Primero vamos a lo que importa:
- Ingresamos al escritorio de Blogger y luego seleccionamos “Diseño” y “Edición de Html”.
- Antes que nada es recomendable hacer un backup del template de nuestro blog, haciendo click en el link “Descargar plantilla completa”.
- Esa cosa rara e ilegible que vemos ahí es el código de nuestro template de Blogger. Buscar la etiqueta <head> (está en las primeras líneas) e insertar el siguiente código a continuación:
<script src='http://www.google.com/jsapi?autoload=%7Bmodules%3A%5B%7Bname%3Agdata%2Cversion%3A1.x%2Cpackages%3A%5Bblogger%5D%7D%5D%7D' type='text/javascript'/>
- Guardar los cambios.
- Luego ir a “Elementos de página”. Noten que la dirección de la página (en la barra de direcciones del navegador) dice “BlogID=xxxx”. Anoten ese número.
- Agregar un gadget (donde más nos guste) del tipo “HTML/Javascript”.
- Escribir un título (ej.:”Hace un año en…”) y como contenido copiar lo siguiente (por temas de formateo lo tengo en tres partes, pegar una detrás de la otra):
<div id="haceUnAnio" style="display:none" ></div> <script type='text/javascript'> <!--
var _blogID = 'xxxx'; function date2RFC3339(dateObject) { var _ = function(text,size){ var out = text.toString(); while(out.length < size) out = '0' + out; return out; } var formattedDate = []; var date = [_(dateObject.getFullYear(),4), _(dateObject.getMonth()+1,2), _(dateObject.getDate(),2)].join('-'); formattedDate.push(date); var time = [_(dateObject.getHours(),2), _(dateObject.getMinutes(),2), _(dateObject.getSeconds(),2)].join(':'); var timezoneOffset = dateObject.getTimezoneOffset(); time = time + (timezoneOffset > 0 ? '-' : '+') + _(Math.floor(Math.abs(timezoneOffset)/60),2) + ':' + _(Math.abs(timezoneOffset)%60,2); formattedDate.push(time); return formattedDate.join('T'); }; function handleErrorPostsDeHaceUnAnio(){}; function handleGetPostsDeHaceUnAnio(feedRoot) { var feedItems = feedRoot.feed.getEntries(); if(feedItems.length==0) return; var boxHTML = '<ul>'; for(var i=0;i<feedItems.length;i++) { var feedItem = feedItems[i]; boxHTML = boxHTML + '<li><a href="'+feedItem.getHtmlLink().getHref()+'">' + feedItem.getTitle().getText() + '</a></li>'; } boxHTML = boxHTML + '</ul>'; var placeHolder = document.getElementById("haceUnAnio"); placeHolder.innerHTML = boxHTML; placeHolder.style.display=''; } function getPostsDeHaceUnAnio() { var minDate = new Date(); minDate.setFullYear(minDate.getFullYear()-1); minDate.setHours(0,0,0); var maxDate = new Date(); maxDate.setFullYear(maxDate.getFullYear()-1); maxDate.setHours(23,59,59); var feedUrl = 'http://www.blogger.com/feeds/'+_blogID+'/posts/default?published-min='+date2RFC3339(minDate)+'&published-max='+date2RFC3339(maxDate); var bloggerService = new google.gdata.blogger.BloggerService('ACP-OneYearAgo-v100'); bloggerService.getBlogPostFeed(feedUrl, handleGetPostsDeHaceUnAnio, handleErrorPostsDeHaceUnAnio); } google.setOnLoadCallback(getPostsDeHaceUnAnio);
--> </script>
- Verán que al principio de todo eso decía “var _blogID = 'xxxxxx';”. Bueno, hay que cambiar las “xxx” por el ID de su blog, que es el número que anotaron en el punto 5 (¿no lo hicieron?, pues retroceden tres casilleros).
- Bueno, eso es lo básico.
¿Qué hace el código? Busca los posts y crea una lista (utilizando los elementos UL y LI) de los links correspondientes en el elemento div que está al inicio del código. Pueden agregarse algo de estilo con CSS a gusto de cada quien.
Algunas aclaraciones. Definitivamente no es el método más eficiente para hacerlo, tiene por lo menos tres contras:
La primera es que necesita la librería cliente de Javascript de Google (ese código que pusieron luego del <head>) y eso es… pesado en relación al uso que se le da. Pero bueno, la librería está bastante optimizada, y si no notan que la carga se hace pesada es soportable.
La segunda es que por más que el código muestre sólo el título y el link a los posts correspondientes, por detrás está pidiendo al servidor los posts completos. Esto es, hasta donde yo sé, inevitable (se escuchan sugerencias). Si la cuestión es que en aquel inspiradísimo fin de semana de 2008 escribimos 5 posts larguísimos… bueno, eso se va a notar.
Y la tercera es que está escasamente probado. Por lo menos en mi caso funciona bien con Chrome, Firefox y IE 8. Tomen sus recaudos y prueben por su cuenta. Cualquier problema comenten y vemos de ir actualizando.
Actualización: arreglado un pequeño error en la línea 34 (al generar el html para mostrar los posts no iniciaba la lista con el tag <ul>, quedando huérfano el </ul> que está más abajo, en la 42.
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.
Jueguitos de Viernes: BowMaster Prelude.
BowMaster prelude es un clásico de apuntar-y-tirar con toques de RPG y estrategia. Nuestra misión es defender el castillo ante el avance de las tropas enemigas. A medida que progresamos en el juego aparecen armas y rivales cada vez más poderosos.
jueves, 11 de junio de 2009
Crisis, ajuste, motivación, productividad, oportunidad.
Corren tiempos de crisis. Traducido al argentino esto implica, aunque sea implícitamente (vía inflación, que aquí el que no corre vuela y el margen de beneficio a corto plazo se valora incluso más que la supervivencia), “ajuste” de salarios. Escribo “ajuste” y no ajuste porque con el entrecomillado pretendo denotar que “ajuste” no es ajuste sino un eufemismo para baja que suena feo. Y es baja y no “congelamiento” o “suspensión de aumentos” debido al pequeño detalle de la inflación mencionado más arriba.
Pero bueno, es lo que marca el mercado y si el golpe de la crisis se distribuye justa o injustamente entre capital y trabajo (o la suposición de que justicia y economía están relacionadas de alguna manera) no es el tema de este post.
Por otro lado, en la industria del del software (y supongo que en cualquier otra cuya base esté mayormente conformada por trabajadores del conocimiento) motivación y productividad están fuertemente relacionadas, si es que no son la misma cosa (¿podría decir que está motivado un programador que no produce? ¿por qué otra razón que no sea falta de motivación podría no producir un programador? Es decir, el que está motivado llega a un resultado, que puede no tener un gran nivel o calidad, pero producir produce).
Mi hipótesis es que si el mencionado “ajuste” afecta a la productividad en esta industria es -por lo menos principalmente- a través de esta forma indirecta, por su impacto en la motivación.
Es un buen momento para recordar aquello de que el motivador más caro es el dinero (creo que es una frase célebre pero no puedo encontrarla, tal vez esté formulada de otra manera). Es por ello que me cuidé de escribir que el ajuste “afecta” a la productividad y no que la disminuye o aumenta, ya que la productividad depende de la motivación y la motivación no necesariamente del dinero.
Me parece una explicación bastante sencilla de por qué hay emprendimientos o proyectos en los que la productividad aumenta considerablemente en tiempos de crisis: hay ajustes monetarios pero la motivación, y por tanto la producción (el desarrollo) se mantiene (o incluso aumenta) y la productividad (la relación entre recursos utilizados y avance obtenido) mejora.
Y también de por qué hay otros proyectos que florecen velozmente una vez pasada la tormenta: si el aumento de productividad es en la práctica una mejora en el proceso de desarrollo el proyecto actúa tal cual un resorte que absorbe el impacto primero e impulsa al equipo, empresa, organización o producto cuando la crisis pasa.
Y de por qué otros proyectos, incluso sobreviviendo a la crisis, no logran volver a ser lo que eran. Hay un punto en el que la motivación cae de tal manera que luego (parafraseando a un gran amigo) “a esto no se lo levanta ni con plata”.
Ejemplo un poco burdo: imaginemos tres equipos enfrentados a la misma crisis. En la práctica (aparte del “ajuste”), lo que sucede es que faltan horas/hombre de analistas funcionales para las pruebas, que el recurso de las horas extra está agotado (es decir, los actuales analistas están agotados) y que no hay posibilidades de contratar más.
-
Equipo I: no hay respuesta. El equipo hace lo que siempre hizo: programar y punto. El proyecto se atrasa, el producto no mejora, se codifica pero no se prueba y las modificaciones no salen, etcétera. Para cuando pasa la crisis hay un retraso importante respecto de la tecnología y de los competidores que han sabido aprovechar o por lo menos capear el temporal.
-
Equipo II: el equipo pone el hombro y se pone a probar. No son buenos probando pero hacen lo posible y la productividad sufre pero las cosas salen. Si pasa la crisis se vuelve al ritmo normal, y tal vez con más recursos se logra recuperar el tiempo perdido, aunque con gran esfuerzo extra.
-
Equipo III: no se resignan a producir menos ni a trabajar más, por lo que buscan la forma de trabajar mejor. Así que buscan la forma no de cubrir las horas faltantes sino de requerir menos horas de pruebas. Descubren (imaginemos que no las conocían) las bondades de la programación orientada al test, herramientas para automatizar tests unitarios y funcionales, reducen costos con máquinas virtuales, programan simuladores de componentes y un largo etcétera. Para cuando la crisis pasa están mejor parados que al principio y cuando se recuperan los recursos éstos se utilizan para hacer más que antes logrando una gran expansión sobre un mercado depurado de competidores.
¿Cuál habrá sido el nivel de motivación en cada equipo antes de afrontar la crisis? ¿Y al final?
miércoles, 10 de junio de 2009
El noble arte del baseball.
Que consiste en
“batear las pelotas tan lejos como sea posible y dar vueltas al tema para hacer puntos”,
inspiradísima frase de “Trabajar por proyectos o la subversión organizativa”, el post de hoy de Sueños de la razón, que les recomiendo.
Twitteado por @Yoriento. BTW: ya sé, esto ya casi es microblogging, qué vergüenza.
martes, 9 de junio de 2009
Las penas son de nosotros, las vaquitas son ajenas.
Burocratismo: Tú tienes 2 vacas. El estado te pierde una, ordeña la otra y luego tira la leche al suelo.
Economía rusa: Tú tienes 2 vacas. Cuentas y tienes 5 vacas. Vuelves a contar y te salen 257 vacas Vuelves a contar y te salen 3 vacas. Dejas de contar vacas y abres otra botella de vodka.
Economía iraquí: Tú no tienes vacas. Nadie cree que no tengas vacas, te bombardean y te invaden el país. Tú sigues sin tener vacas.
Capitalismo moderno (éste es genial): Tú tienes 2 vacas. Vendes 3 de tus vacas a tu empresa que cotiza en bolsa mediante letras de crédito abiertas por tu cuñado en el banco. Luego ejecutas un intercambio de participación de deuda con una oferta general asociada con lo que ya tienes las 4 vacas de vuelta, con exención de impuestos por 5 vacas. La leche que hacen tus 6 vacas es transferida mediante intermediario a una empresa con sede en las Islas Caimán que vuelve a vender los derechos de las 7 vacas a tu compañía. El informe anual afirma que tu tienes 8 vacas con opción a una más. Coges tus 9 vacas y las cortas en trocitos. Luego vendes a la gente tus 10 vacas troceadas. Curiosamente durante todo el proceso nadie parece darse cuenta que, en realidad, tú sólo tienes 2 vacas.
Hay más en Diario de Nunca Jamás, al que llegué vía Alt1040. Para que nadie se la pierda, el título hace referencia a la letra de El Arriero, de Atahualpa Yupanqui.
El modelo de negocio de las consultoras frente a la crisis.
Llego un poco tarde (pero seguro) a Cuando la crisis golpea una consultora IT, en el blog de Julio César Pérez Arques.
La analogía con la “máquina expendedora” es una muy acertada descripción del modelo de negocio de las grandes consultoras que explica muy bien cómo se están viendo afectadas por la crisis.
“Existe una necesidad: el código. Y las consultoras hacen negocio con máquinas expendedoras de código. El código es un bien caro y se genera directamente dentro de las máquinas. Sin necesidad de comprar ninguna materia prima.
Que un cliente necesita código, pues negocias cuantas máquinas de cada tipo le pones, por cuanto tiempo y, ala, a facturar.
Las máquinas no tienen coste inicial, sólo de mantenimiento (salario) pero tampoco es que sea mucho. Como cualquier coste, se intenta reducir al máximo. No tiene mucha más preocupación. La prueba está en que existe una generalizada inversión de salario, donde no siempre a una máquina que aporta mayor valor le corresponde un mayor coste (salario).”
lunes, 8 de junio de 2009
El verdadero significado de los nombres de versión.
…o por lo menos así lo ve Iván Ramírez en OnSoftware, donde nos ilumina:
-
0.xx: Me da vergüenza llamar programa a esto.
-
Unstable: Contiene las novedades interesantes, si es que funcionan.
-
Standard: La versión pro era demasiado cara así que esta intermedia debería comprarla alguien.
El resto en la entrada original.
domingo, 7 de junio de 2009
Defiende tu puesto…
…con este poderoso lanzalápices fabricado con elementos de oficina.
How to Make a Office Gun - The best bloopers are a click away
Cuidado... es peligroso en serio, no digan que no les avisé.
Visto en Ikkaro y compartido por Nicolás.
viernes, 5 de junio de 2009
Jueguitos de viernes: 3x1 (Love Overdose, Dung y Savanna Blocks).
Javier tuvo la deferencia de hacerme perder la tarde del jueves enviándome tres simples y adictivos juegos de su autoría.
El primero (y el que más me ha gustado) es Love Overdose, un clásico shooter en el que debemos impedir que los osos de peluche crucen la pantalla. Un hint: se gana más puntos desmembrándolos primero y disparando a la cabeza después.
Cuando se aburran de cultivar el morbo pueden probar Dung, en el que personificamos a una mosca hambrienta de mierda popó acosada por una horda cada vez más numerosa de insectos.
Y para el final una nueva variación del Tetris perfecto para madres, hermanas, amigas, novias o esposas (por cierto ¿se acuerdan de este video?), Savanna Blocks.
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.
martes, 2 de junio de 2009
Ladran Sancho, señal de que cabalgamos.
Y nada más cierto, sobre todo si hablamos de desarrollo de software. Con los años me acostumbré a interpretar como nubes en el horizonte lo que para otros es motivo de festejo. ¿Todo avanza sobre ruedas? Mmmm… mala señal. Pongamos por caso…
…que todo comienza con la típica frase apocalíptica “Esto es una pavada”. Que leemos o nos comentan los requerimientos y todo parece sencillo…
…que a partir de ello hemos elaborado una estimación y vuelve a las pocas horas con un escueto “ok”. ¿Ok? ¿No falta nada? ¿No nos olvidamos de algo? ¿No les parce demasiado, o demasiado poco, o muy caro, o muy barato?
…que luego el diseño va surgiendo sin graves problemas, y que después de remover algunas piedras y hacer un par de ajustes menores se termina todo en tiempo y forma.
…que las pruebas no arrojan errores de gravedad…
¿Qué clase de proyecto es ese? Uno que va a explotar como fuegos artificiales.
Lo primero que se me ocurre para justificar lo anterior tiene que ver con las zonas grises, los cambios, los errores.
Todavía suponiendo que -en un proyecto muy particular- los dos primeros elementos –zonas grises y cambios- no estén presentes, el tercero –errores- estará siempre ahí. En cualquier metodología -ya sea ágil, evolutiva o lineal- se establecen puntos de control a lo largo de todo el proyecto y las tareas son divididas –o deberían serlo- de manera tal de que todos los componentes pasen a lo largo del proceso bajo la mirada –supuestamente atenta- de por lo menos dos pares de ojos (aunque con par y medio alcanzaría). Si todo va más o menos bien surgirán incongruencias, malos entendidos, omisiones, errores de interpretación, de diseño, de codificación y de cualquier otro tipo imaginable. Si todo va realmente bien se irán corrigiendo con el tiempo, aunque aparecerán nuevos problemas. Por eso su ausencia es clara señal de que algo anda mal: la ausencia de ladridos indica la ausencia, la distracción o la indiferencia de los perros.
Los sistemas informáticos –incluso los más triviales- son lo suficientemente complejos como para que un componente menor o una situación improbable mas no imposible tire todo abajo. Incluso sin llegar a ese extremo tenemos que recordar que el software en su conjunto –sobre todo el de gestión- es necesariamente un subsistema de un sistema mucho mayor, una organización (el “negocio”). Desde este punto de vista no es más ni menos que un servicio para esa organización. Como pasa con todos los servicios, solamente notamos su presencia a través del sinfín de problemas e incomodidades que surgen cuando andan mal. Y ahí sí que aparecen los perros –pero ahora mostrando los dientes- y será mejor que cabalguemos rápido.
Un ejemplo simple: la falta de un calefactor o uno averiado en invierno puede interrumpir el trabajo por varios días, con todos quejándose amargamente. Si esto no sucede –las quejas, quiero decir- es que a) no hay trabajo que interrumpir, b) la calefacción no hacía falta, c) todos murieron congelados, d) se fue todo el mundo a casa o al bar de la esquina, o… en fin, en todo caso vemos que no es tan malo enterarse de que algo no anda, que peor es hacerlo a último momento o, sobre llovido mojado, que nadie se queje o que las quejas se aplaquen solas ante la falta de solución (¿resignación? ¿abandono?).
Luego, la famosa frase –que no es del Quijote, según revela el oráculo- se aplica también a nivel de empresa u organización… y no necesariamente en cuestiones de infraestructura o servicios: pensemos en la diferencia entre las reacciones ante cada pequeña o gran caída de Google o pifie en Microsoft (el mundo entero se transforma en una gran jauría enfurecida) y el lento, continuo y casi silencioso desangrarse de Yahoo!
Así que ¿problemas? ¿quejas? Cabalgamos.