Thinking Recursive

November 23, 2006

english.gif My first attempt to write something in English (Don´t cut me some slack!)

Basically, the programs written in functional languages like Lisp or Haskell are based on function definitions. I don´t want, by no means, to give a thorough explanation of the features that functional languages offer because, among other things, I am not an expert on such languages (although I´d like to). I am writting this because not until I tried to implement some probs kinda recursive-natured, did I find how quick & fun this kind of programming style is.
Let´s set out to implement a naive algorithm for sorting lists in Haskell, so for a given input list, we get the corresponding sorted list. The function sort takes an argument of type list and returns a new list representing the former one in order.

-- Sort a list
sort :: Ord a => [a] -> [a]
sort [] = []
sort (x:xs) = insert x (sort xs)

The first line comprises the function definition, which takes a list of elements of type a, and returns the same type ([a] -> [a]). The string “Ord a” restricts the potential members of the list to types compliant with the ordering rules (integers for instance). But this is Haskell specifics, not very appealing. Let´s go to the implementation. The second line is the implementation of the function for an empty list, which in turn returns an empty list.
Lokking over the last line, we can notice that it defines how a non-empty list, (x:xs) being x the first element and xs the remainder, is going to be sorted. For that, it makes use of a new function named insert. Here you are:

-- Insert an element in to a list, maintaining the order
insert :: Ord a => a -> [a] -> [a]
insert y [] = y:[]
insert y (m:ms)    | y <= m = y:(m:ms)
                   | y > m     = m:(insert y ms)

This second function takes two arguments, the first one being an element of type a, and the other a list of elements of the same type. Same type restrictions as in the parent function. The return value is another list. The trivial case (insert y [] = y:[]) concatenates an element with an empty list. The general case says (last two lines): “if the element to insert is less or equal than the first element in the list, concatenate them. Otherwise, join the first element in the list with a recursive call using the same element and the remaining elements in the list”. Easy ha!

Let´s see the recursive call stack for this execution:

sort [9, 3, 5]
       |
insert 9 (sort [3, 5])
       |
insert 9 (insert 3 (sort [5]))
       |
insert 9 (insert 3 (insert 5 (sort [])))
       |
insert 9 (insert 3 (insert 5 []))
       |
insert 9 (insert 3 5:[])
       |
insert 9 (3:5)
       |
3:(insert 9 5:[])
       |
3:5(insert 9 [])
       |
3:5:9:[]
       |
[3, 5, 9]

Yay! Some may argue that this implementation is time-taking, that it does too many recursive calls even for small inputs. A complete resource waster. I couldn´t do anything else but agree with that, but, you know what? I don´t care! It´s just this code´s neatness what makes me feel thrilled. I look at it and I think: “Ey dude! It´s as pretty as a picture!”. If you still feel like sprucing it up, a good starting point could be this one.

Another good point in the functions above is that I didn´t say that the input list had to hold numeric elements! Surprisingly (not for an expert), the following execution outputs a nice result (guess why?):

sort ["Water", "The", "Dead", "In"]

... (call trace left out)

["Dead","In","The","Water"]

For the most part, this is what I wanted to show here. Isn´t it worth the hassle? At least for me, no doubts.


Cuidado con el SQL Injection

November 10, 2006

Un reciente post de Joel explicaba como hacer SQL Injection. Voy a extender aquí un poco la información.
Imaginemos que somos muy malos programadores y tenemos el siguiente código:

String query=”select user, pass from user where user='”+user+”‘ “;

Si introducimos como usuario pepito, tenemos que se ejecuta:

select user, pass from user where user=’pepito’

¡Fenomenal! …¿Fenomenal? y si en vez de usar pepito pongo ‘;drop table xxxx; — , ¿Qué pasa?

select user, pass from user where user=”; drop table xxxx; — ‘

¡Fenomenal! Nos acabamos de cepillar la tabla xxxx;

Vale, entonces es cuando pensamos, pues filtramos todos las comillas simples. Y entonces es cuando viene nuestro mejor cliente, el señor O’Brian y decide que si nuestro sistema no acepta su nombre quizás es que no merecemos que se gaste dinero en nosotros. Así que bueno, aceptamos ‘, pero sustituimos ‘ por ” en todos los literales.

Encones tenemos algo así:

insert into xxxx values … O”Brian ….

Y en BD queda como O’Brian.

Joder. ¡Somos buenísimos! Hemos arreglado el problema del SQL Injection con sustituir ‘ por ” doble… ¿o no?

Imaginamos que una vez tenemos un usuario en nuestra BD, luego utilizamos dicho dato para hacer otras queries a su vez (Ej.:”Dame todas las compras del usuario X”). Como solo securizamos las queries que tenían entrada de usuario, resulta que seguimos teniendo la siguiente query:

query=”select * from compras where usuario='”+usuario+”‘”;

¡Y ya la tenemos montada otra vez!

Resulta que como protegemos la query de entrada pero, una vez almacenado el registro, guardamos el texto sin proteger, tenemos otra vez el mismo problema.

Conclusión 1 : Protege TODAS LAS QUERIES, no solo las que crees que son de entrada.

¿Filtramos solo las comillas simples?

Bueno, quizás tengas algo así:

String id= xxxxx

query=”select * from tabla where id=” +id;

Siendo el id numérico en la BD. Pues ya la tenemos montada, no nos hacen falta comillas para nada.

Podemos dar a id el valor de id, o algo peor id; drop table xxxx; o quitamos el resto del filtrado con — o /** **/ (Comentario en SQL) ,etc.

Otra: en mysql por ejemplo 9e3 es 9000, cuidado con pensar que limitando el tamaño del parámetro podemos conseguir algo…

Conclusión 2: Son vulnerables TODOS los campos, repasa todos y cada uno de los campos

Otro error común es pensar, bueno, nuestro modelo de datos es complejo, nadie va a adivinar el nombre de las tablas y de los campos…¡MAL! Pueden sacarlos si los errores de BD se sacan directamente al usuario. Ahh vale, tu siempre rediriges a una página de error, estas a salvo…¡EEEERRROOOOOR! Se pueden sacar por inferencia. ¿Cómo? Con sentencias condicionales:

select case when condicion then ‘menor’ else ‘mayor’ end;

Es decir, devolvemos un texto o otro dependiendo de la condición.

Así la condición puede ser, dame el primer bit de cierto campo. Y el then/else un elemento de diferenciación. Donde la “diferenciación” pueden ser varias cosas:

  • Esperas de tiempo diferente
  • Variar la respuesta
  • Devolver error en uno y en otro no (Ejemplo: el campo a devolver es un numérico y en uno devolvemos 3 y en otro ‘caca’ o más fácil: “select case when condición then 37 else 37/0”)

Además pueden atacar a las tablas de metadatos donde se almacenan las descripciones de las tablas, etc.

Por tanto…

Conclusión 3: Nuestro sistema no es invulnerable porque “ocultemos” los nombres de tablas y campos.

Hay unos cuantos caracteres más que filtrar. SQL es un lenguaje y por tanto hay muchas formas de hacer las cosas, así que si usas Java lo mejor es utilizar Prepared Statements.

Un presentación sobre el tema aquí.

UPDATE: Rubén me envía este enlace. Guía para desarrollo Web seguro. Imprescindible.


Jetty Embebido

November 8, 2006

Una característica muy interesante del servidor Jetty es que puede ser embebido en cualquier aplicación JAVA. Esto nos permite hacer diversas cosas: Desde dar una interfaz de configuración Web a una aplicación no Web, hasta cosas más exóticas como realizar testing de un cliente http.

Lo mejor que tiene es que es facilísimo poner un servlet a escuchar en una url, vamos, cosa de niños. Bueno, no me enrollo más, aquí va el código:

import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;
...
server= new Server(8080);
Context root = new Context(server,"/contexto",Context.SESSIONS);
root.addServlet(new ServletHolder(new Servlet(){
 public void destroy() {}
 public ServletConfig getServletConfig() {return null;}
 public String getServletInfo() {return null;}
 public void init(ServletConfig arg0) throws ServletException {}
 public void service(ServletRequest request, ServletResponse response)
         throws ServletException, IOException {
     response.getOutputStream().write"HOLA MUNDO"getBytes());
 }
}), "/servlet");
server.start();
 ...

Y ya tenemos nuestro servlet escuchando en http://localhost:8080/contexto/servlet. Este servlet tampoco hace mucho, pero da una idea de lo fácil que puede ser poner a escuchar un servlet en un Jetty embebido.


Planificando de forma Ágil (2)

November 7, 2006

Después de un breve descanso bloggero (manda narices, después de escribir solo un post :D) prosigo con la segunda parte del post sobre planificación ágil. Si no leíste el primer post sobre planificación ágil, te recomiendo encarecidamente que lo leas. Lo puedes consultar aquí.

Este post esta totalmente basado en uno de Joel Spolsky, concretamente éste; y mi experiencia de haberlo puesto en práctica. Te recomiendo leer no solo ese post, sino cada uno de los posts que tiene en su blog, ¡Son buenísimos!

La solución

¿Planificación Ágil? Sí, es lo que tiene el término “ágil”, ahora todo lo que tenga que ver con el desarrollo de software tiene que ser ágil: metodologías ágiles, testing ágil, agilidad ágil… Así que, ¡Qué demonios! Planificación ágil, o como no morir en el intento de planificar un proyecto.

¿Cómo planificamos? Pues de la forma más fácil…¡Con Excel! No necesitamos tener ninguna herramienta hipercompleja (leáse Ms Project), no necesitamos pagar miles de euros. Lo único que necesitamos es una mínima organización, y esto lo conseguiremos con Excel y una cierta disciplina.

Eres un fanático del Open Source, un vago y maleante que solo utiliza ese sistema operativo de hackers llamado Linux. ¿ Solo se puede planificar ágilmente con un producto de MS ? ¡No! Todo lo que cuento se puede realizar perfectamente con Open Office, de hecho tengo dos versiones, una para OO y otra para Excel. La única limitación es que, aunque OO permite tablas pivotadas, no permite gráficas pivotadas. Pero bueno, esto es un poco pijada (o no…), además supongo que en versiones posteriores de OO incluirán gráficas pivotadas. ¡Así que ya no hay excusa! ¡Arranca tu LFS y planifica tus proyectos de forma ágil con OO!

La forma más sencilla de organizar las tareas de un proyecto sería realizar una lista de TO-DOs (Cosas por hacer), esto lo podemos hacer con un simple fichero de texto, ¡Incluso con papel y boli! (No olvidéis nombrar tan avanzada herramienta la próxima vez que os pregunten en una entrevista que utilizáis para planificar). Aunque una lista de TO-DOs es válido para proyecto pequeños, enseguida vemos que necesitamos “catalogar” dichas tareas, repartirlas entre los miembros del equipo, estimar horas, etc. Por tanto, en vez de utilizar un fichero texto conviene utilizar una hoja de cálculo, lo cual nos va a permitir extender nuestro “modelo de datos” del planificador.

Partiendo de la idea inicial de usar una lista de tareas podemos extender “el modelo” con los siguientes campos:

  • Funcionalidad: Una funcionalidad es algo visible para el cliente, una historia de usuario si nos ponemos XP-ianos. Una funcionalidad va a estar compuesta de tareas.
  • Tarea: Nuestro elemento más básico, un elemento de nuestra primitiva lista de TO-DOs.
  • Número secuencial: Útil para referenciar la tarea de forma unívoca. Imagina que tienes una tarea “Documentación módulo RSP” y otra “Documentación módulo SRP”, ¿Verdad que no querrás que haya una confusión porque alguien creyó leer otra cosa?
  • Estado: ¿La tarea ha comenzado? ¿Se terminó? Es importante guardar el estado de cada tarea. El estado debe ser catalogado (PENDING, STARTED, DONE) para permitir su filtrado. Si además incluimos un formato condicional (Si “PENDING” entonces color rojo) miel sobre hojuelas.
  • Criticidad: ¿Tiene el mismo riesgo poner una imagen en un jsp que la conexión con el backend de nóminas? Seguro que el cliente piensa que cambiar el jsp es más importante, pero esta tarea no tiene ninguna dificultad y la podemos afrontar en cualquier momento. Por tanto asignaremos una criticidad CATALOGADA (LOW, MID, HIGH) a cada tarea. Con esto podremos hacer preguntas tan útiles como: ¿Cuántas tareas críticas están pendientes o sin terminar? Vaya, parece que esto del Excel empieza a dar su juego ehh.
  • Persona: Sería fantástico hacer como Juan Palomo: yo me lo guiso, yo me lo como. Pero normalmente tenemos un equipo de proyecto, y por tanto las tareas las realizarán diferentes personas. Por tanto, añadamos una columna que diga quién realizará la tarea x. Este campo también debe ser CATALOGADO. ¿Por qué? ¿Cuántas formas hay de escribir mi nombre? M. Alcón, Miguel Alcón, Mikel Alcón, Mikel Halcon ( Sí, estoy harto de responder: “Alcón, sin H“). En definitiva, si tenemos un catálogo de desarrolladores, únicamente nos permitirá elegir entre los desarrolladores de la lista. Ahora ya podemos hacer unas cuantas preguntas más a nuestro planificador: ¿Cuántas tareas tiene pendientes el desarrollador X? ¿Quién tiene las tareas críticas?, etc.
  • Estimación original: ¿Cuánto tiempo estimamos que durará la tarea? Planifica siempre en horas y no asignes más de 30 o 40 horas a una tarea, cuantas menos mejor ¿Por qué? Para obligarte a definir el plan con un nivel alto de detalle. Recuerda que huíamos de las planificaciones del estilo “Diseño 2 semanas”. Es más fácil acertar si sabemos exactamente qué vamos a realizar, en esto consiste realmente la planificación ¿Verdad?
  • Estimación real: El que tiene boca se esquivoca equivoca, y planificando te equivocarás, y al principio mucho más. Así que actualiza la estimación real a medida que avanza la tarea. Si pensabas que la tarea x iba a durar 10 horas, pero ahora, con el framework Hiberspring AOP Edition 3.0.1.5, lo puedes realizar en 5 horas; o al revés, te has encontrado con un imprevisto, ¡Actualiza la estimación real! Nos permitirá tener una visión real del estado de la tarea.
  • Tiempo consumido: ¿Cuántas horas llevas consumidas en la tarea? Este campo nos permitirá preguntas tan interesantes como: ¿Cuanto le queda a esta tarea? (Estimación real – Tiempo Consumido) ¿Cuánto le queda al proyecto en general? (SUM (Estimación Real) – SUM (Tiempo Consumido)) ¿Cuántas horas le quedan en tareas asignadas al desarrollador X?
  • Restante: Hemos visto que el tiempo que le queda a una tarea es Est. Real – Tiempo Consumido. Por tanto añadamos una columna con ese valor calculado automáticamente.
  • Desvío: Entre la estimación original y la real hay una diferencia. ¿Cuántas horas más ha costado esta tarea? Precalculemos el valor Estimación real – Estimación original.
  • Desvio %: ¿Es lo mismo retrasarse 2 horas en una tarea de 1 hora que en una tarea de 20? No. Pues entonces precalculemos este valor: Estimación real / Estimación original y lo pondremos en formato porcentual:
    • 100 % : Estimación Real = Estimación Original. Perfecto
    • <100 %: ¡Hemos tardado menos! Cliente contento y nosotros planificaremos de forma más precisa la próxima vez.
    • >100% : Empiezan los problemas, ¿La tarea es crítica? ¿Quién la tiene asignada? Utilizaremos el formato condicional para asignarles colores de fondo dependiendo del desvío (Ej.: Amarillo 101% a 105%, Naranja 105% a 115%, Rojo >115%)

Ahora ya podemos saber cuáles son las tareas que más retrasos están produciendo, tanto en niveles absolutos como porcentuales, sabemos cuáles son los desarrolladores que más se están retrasando, cuánto tiempo le queda al proyecto, etc. En definitiva, tenemos controlado el proyecto.

La cosa podría quedar más o menos así:

planificacion

Como habrás podido comprobar la mayoría de los campos son catalogados. Esto nos permitirá realizar agrupaciones y filtrados de los datos.

¿Se pueden hacer cosas más complejas ? Sí, poniendo fechas de inicio y de fin, tareas bloqueantes, etc. Lo único que tienes que tener en cuenta es que deberás utilizar macros de Excel, no pongas este tipo de campos para ser modificados manualmente, porque conseguirás que te dé pereza actualizar la planificación. Lo campos a rellenar deben ser los mínimos y que no requieran pensar más que en la planificación. Nada de “en qué fecha me pongo si la tarea dura 30 días y hay un fin de semana de por medio”. O utilizas una macro o no utilices estas fechas. Además para la mayoría de proyectos este tipo de fechas no te van a servir para nada. Tu objeto de medida es horas que quedan de desarrollo.

Tampoco hay que emocionarse y empezar a añadir campos a diestro y siniestro. Añade según necesites. De la misma forma, solo añade métricas calculadas que realmente te sean útiles, no añadas algo del estilo (Estimación Original – Estimación Real)*Número de letras del nombre del desarrollador/ PI. Recuerda, puedes ir evolucionando el “modelo de datos” según lo necesites.

Una vez que tenemos la parte técnica resuelta queda la parte metodológica… ¿Cómo coño usamos esto!? Las directrices a seguir serían éstas:

  • Que los desarrolladores que van a realizar las tareas sean los que, generalmente, hagan la estimación original. ¿Por qué? Por dos motivos, el primero que nadie sabe mejor cuanto va a costar hacer un trabajo que la propia persona que va a hacerlo. El segundo motivo es más psicológico: para que la persona asignada se responsabilice de su tarea. Nunca te podrá decir algo como ¡Me diste poco tiempo!.
  • Cada mañana actualiza la planificación. Tu protocolo a seguir cada mañana debería ser el siguiente:
    • Café (Imprescindible, sustituible por té, colacaos, infusiones y bebidas calientes varias. Podrá ser acompañado de galletas, donuts o cualquier artículo de bollería industrial)
    • Reunión con los miembros del equipo, de la siguiente forma:
      • ¡DE PIE! ¿Por qué ? Las reuniones de pie tienden a ir a lo concreto, nadie aguanta mucho sin asentar las posaderas. Si no me crees, compruébalo quedándote de pie 2 horas seguidas, es una experiencia única.
      • Con pizarra. Ayuda a exponer lo que tenemos en mente sin perder el tiempo ni hacérselo perder a los demás. Todo queda más gráfico con un dibujo y minimiza el esfuerzo del resto del equipo para comprendernos.
      • Cada miembro expone el estado de sus tareas y las modificaciones a la planificación (Estimación real y consumido). El jefe de proyecto actualiza la planificación.
      • Se asignan nuevas tareas
      • ¿Dudas? ¿Bloqueos entre tareas? Se comentan y se anotan en la hoja Excel.
    • El jefe de proyecto actualiza la hoja Excel y recalcula las métricas.
    • Consultar las métricas para ver el estado del proyecto, si no es periódicamente por lo menos hacerlo regularmente.
  • Repito, planifica a nivel de horas, no caigas en la tentación de hacer planificaciones a nivel de días, semanas o incluso peor, meses. Perderás el control del proyecto y te estarás autoengañando.
  • Evita los bloqueos entre tareas. No querrás tener desarrolladores parados porque la tarea x no ha terminado, ¿Verdad?
  • Evita que los recursos compartidos se conviertan en elementos bloqueantes:
    • ¿La BD, que usa todo el equipo, se ha caído? ¿Alguien ha cambiado el modelo y los demás no tienen dichos cambios? Seguro que te suena este tipo de problemas. Pon una BD para cada desarrollador, si no puedes permitírtelo crea protocolos que eviten o minimicen estos problemas.
    • Cada desarrollador debe poder ejecutar el código en su propia máquina (Sea un exe, un aplicación web con Apache o con un servidor de aplicaciones por cada desarrollador). No hay excusas, el coste es ridículo.
    • ¡Usa control de versiones! Lo mismo, no hay excusas. Y que nadie, absolutamente nadie suba algo al repositorio sin haber probado tanto sus cambios como la integración con el resto.
    • Relacionado con lo anterior. Genera pruebas unitarias, de regresión, validación, integración etc. Que puedan ejecutarse de manera automática. Imprescindible. Si lo unimos con el punto anterior tenemos que: Nadie, absolutamente nadie, suba algo en el repositorio sin que las pruebas pasen correctamente. He visto equipos de más de cinco personas paradas porque la versión en el SCM tenía un fallo bloqueante.
  • Evita los bloqueos dentro de tareas: Este es la principal fuente de retrasos en los proyectos (Además de los proyectos mal planificados, of course). Programadores que se quedan atascados durante horas con un problema. Haz ver a los miembros del equipo que no están solos en su cruzada, si alguien se bloquea durante más de 15 minutos con un problema ¡Que solicite ayuda a alguien del equipo! Tampoco es cuestión de que el equipo entero se ponga a buscar durante dos horas el carácter “<” que falta en un html, pero generalmente alguien conoce la solución porque ya se enfrentó con ella antes. Eso sí, con un límite, no es cuestión de que la misma persona esté ayudando todo el rato a los demás, dejando su trabajo de lado, o de que se pase dos días intentando solucionar el problema de otro.
  • Añade una tarea “debug”, que sirva de colchón ante imprevistos. Al final del proyecto haz la resta Estimación Original Debug – Desviación Horas Total Proyecto, te ayudará a dar un tamaño correcto a la tarea colchón en siguientes proyectos. ¿Qué tamaño debe tener? Debería tener, al menos inicialmente, un tamaño no menor al 50% de horas del proyecto.
  • Utiliza las tablas pivotadas para calcular totales y subtotales (Total de horas por desarrollador, total de horas por tarea,etc.). Usa las gráficas pivotadas: de un vistazo podrás ver el estado del proyecto.

Espero que estos consejos os permitan planificar mejor (o simplemente planificar). Lee el artículo de Joel, es mucho más completo. Además, yo intento escribir con gracia, pero solo él lo consigue :).

Queda pendiente de subir un fichero Excel de ejemplo como adjunto. Dada mi nulidad blogeril, tendré que investigar cómo hacerlo.