jueves, enero 10, 2013

El desastre del Ariane V y la importancia de los tipos de datos


El 4 de junio de 1996, tras 12 años de desarrollo y tras una inversión de seis millones de euros, finalizaría la cuenta atrás para que la Agencia Espacial Europea llevara al espacio la lanzadera espacial Ariane V. Ese día, nada indicaba que algo podría salir mal. El desarrollo de ese monstruo tecnológico había superado todos los exhaustivos controles a los que fue sometido, el personal que lo desarrolló estaba perfectamente cualificado, se habían seguido al ciento por cien los protocolos… Sin embargo, algo fue mal tras el lanzamiento. Ante la atónita mirada de todos los controladores aeroespaciales, el Ariane V dejaba a un lado el recorrido previsto para tomar uno imprevisto. Tras mucho pesar y ante el peligro que suponía el nuevo rumbo tomado por la lanzadera espacial, ésta tuvo que ser destruida. Un estudio posterior determinó que el fallo se produjo como consecuencia de un sonoro error de programación; se produjo un desbordamiento al tratar de convertir un valor expresado en coma flotante de 64 bits a otro valor definido como entero de 32 bits.


Independientemente de lo bochornoso que puede resultar que un tonto error de programación eche al traste semejante inversión, ese suceso es un claro ejemplo de una de las más grandes deficiencias en el mundo del desarrollo del software: el peligroso menosprecio que sufre la definición y el manejo de datos. El ansia por reducir tiempo y costes hace que en numerosas ocasiones los desarrolladores de software se limiten a realizar un producto cuyo único requisito es que funcione. Así, se limitan las pruebas realizadas, se olvidan etapas de análisis, no se refina el diseño, se documenta únicamente lo "imprescindible", y se reduce la definición y manejo de datos a lo que el lenguaje de programación proporcione de forma nativa. Al respecto del fallo del Ariane V, sin conocer exhaustivamente dicho fallo, uno se extraña de que se intente hacer una conversión de datos entre dos tipos de datos que a priori no tienen nada que ver, tanto en tamaño como en estructura.

Uno de los principios básicos a la hora de diseñar tipos de datos es limitar su alcance y establecer las estructuras apropiadas para mantener la integridad de los datos. Para ejemplificar eso, podemos pensar en un tipo de datos que sirva para almacenar números enteros en un espacio de ocho bytes y lo que pasaría si tratáramos de almacenar en él un número entero de doce bytes. Si no hay nada que avise o evite semejante torpeza, estaremos manejando sin darnos cuenta datos erróneos. Muchos programadores defienden que no hace falta hacer ese tipo de controles puesto que se supone que un buen programador no va a cometer esos errores. Sin embargo, hay que pensar que la programación no deja de ser una tarea humana y por tanto sometida a nuestras imperfecciones. También hay que pensar que en numerosas ocasiones la labor del programador se ve influenciada por un entorno tecnológico y funcional normalmente hostil que suele reaccionar de forma imprevisible, e influenciado por unas limitaciones presupuestarias que no ayudan para nada a la claridad de ideas. Tampoco podemos olvidar que no todos los datos siguen una estructura tan simple como un entero.


Un buen diseño de un tipo de dato no sólo puede servir para evitar perder millones de euros por culpa de un error tonto. También puede servir para optimizar el tiempo de ejecución de un programa. Una de las frases más sonadas en el mundo de la programación es aquella que dice que un programa se define por su algoritmo más sus datos. Es decir, no nos sirve de nada tener un algoritmo muy eficiente, si el programa se tira media vida en almacenar un dato. Podemos imaginar dos alternativas de diseño para una lista ordenada: una con un puntero que apunte al primero de sus elementos y otra con dos punteros apuntando al primer y último elemento respectivamente. La operación más costosa para la primera lista sería añadir un elemento al final de la lista, puesto que habría que recorrer todos los elementos de la lista antes de poder insertar el nuevo. En cambio, la operación más costosa para la segunda lista sería insertar un elemento en mitad de ella. En el primer caso hablaríamos de una complejidad O(n) y en el segundo de una complejidad O(n/2), el doble de eficiente. Siguiendo la idea de lograr una mayor eficiencia a partir del diseño del tipo de dato, debemos de pensar que la propia estructura interna del tipo de dato puede ayudar a obtener una respuesta más rápida a según qué necesidades del programa. Volviendo al ejemplo de la lista ordenada, ésta va a ser una muy buena estructura de datos si una operación recurrente sobre ella es el obtener de forma ordenada los elementos de la lista. 


La última ventaja que me gustaría destacar acerca de las bondades de hacer énfasis en el diseño de una estructura de datos es que nos puede permitir manejar niveles de abstracción más cercanos a nuestra forma de entender la realidad. Si, por ejemplo, modelizamos algo como si fuera un grafo y lo implementamos con sus operaciones de escritura y lectura, podremos entender mejor lo que hace la funcionalidad de un programa que si no hacemos una modelización. Siempre es mucho más fácil enfrentarse a un par de procedimientos de entrada y salida que pelearse con un entramado de variables, punteros, y que sé yo. Es curioso ver que este aspecto que en principio no parece importante sea el aspecto más diferenciador entre los dos paradigmas de programación más usados, el orientado a flujos de datos y el orientado a objetos. Mientras los más puristas defensores del primero defienden a capa y espada el no realizar ese tipo de interfaces, los respectivos del segundo defienden el usar interfaces para todo. Independientemente de que ni unos ni otros tienen razón puesto que la mejor solución suele venir del correcto equilibrio entre extremos y de que en este mundillo las elecciones siempre vienen determinadas por las características del producto final, esto nos lleva a darnos cuenta de la olvidad importancia de los tipos de datos.
 
Volviendo al Ariane V, el dinero que se perdió como consecuencia de su variación de rumbo nos da una visión de una sociedad obstinada en pensar a corto plazo. En pos de ser más competitivos se reducen costes a costa de aumentar la incertidumbre en el largo plazo. En ocasiones nos encontramos con consecuencias muy llamativas como la relatada. Sin embargo, la mayoría de las consecuencias de este tipo de "recortes" aparecen en momentos de estricta intimidad entre el programador y el código ajeno a él. Muchas veces los costes de mantenimiento de un sistema informático se disparan por no querer emplear tipos de datos que permitan adaptar modificaciones fácilmente, o  por no hacer controles de integridad de los datos. En ese sentido, es anecdótico recalcar que los grandes defensores del lenguaje 'C', uno de los lenguajes de programación más populares en la historia de la informática, destaquen como una de las virtudes de dicho lenguaje las facilidades que da para no controlar la integridad de los datos.

Chechu,

10/01/2013

No hay comentarios: