En busca del software perfecto
¿Qué tienen en común un celular, un marcapasos y un auto? Todos estos aparatos están controlados por software, que fue previamente diseñado y testeado por un equipo de especialistas. Sin embargo, más allá de su importancia central en la vida moderna, el software tiene un problema universal: los errores. Cuáles son los principales desafíos que enfrentan los ingenieros de software para poder mejorar los programas a pesar de estas fallas comunes.
“El software se está comiendo al mundo”, dijo Marc Andreessen en una famosa nota del periódico Wall Street Journal. Esa frase del fundador de Netscape hacía referencia a la revolución e impacto que ha producido el software y las tecnologías disruptivas en todos los ámbitos de la vida moderna. Tomando la metáfora de Andreessen, para que el software no devore sus propias soluciones, desde hace medio siglo se trabaja sistemáticamente en una disciplina que trasciende la actividad de programar.
Según la definición del IEEE (Institute of Electrical and Electronics Engineers) la ingeniería de software es “la aplicación de un enfoque sistemático, disciplinado y cuantificable al desarrollo, operación y mantenimiento del software”. Incluye el desarrollo de programas e integra a las matemáticas, la computación y algunas prácticas cuyos orígenes se encuentran en la propia ingeniería. El concepto nació en 1968, tras una conferencia en Garmisch (Alemania) que tuvo como objetivo resolver los problemas de la crisis del software, a partir de los avances de nuevo hardware basado en circuitos integrados, ocurridos hasta mediados de la década de 1980.
Para entender el concepto de ingeniería de software, también se puede trazar un paralelismo con la ingeniería civil (salvando las distancias). De la misma manera que construir un edificio no es solo apilar ladrillos, escribir programas no es lo único que se hace cuando se construye un sistema de software. Para los edificios, existe todo un proceso de diseño, planos, pruebas estructurales, etc. En tanto que, para construir software, además de la programación, se deben coordinar tareas de diseño, testeo, validación, planificación de grupos de trabajo, implementación, entre otras.
Una joven disciplina
A diferencia de las ingenierías tradicionales, la ingeniería de software posee apenas cincuenta años de vida. En este escenario, los ingenieros de software buscan repensar qué aspecto debe tener el plano de un software, qué tipo de pruebas matemáticas y estructurales se necesitan realizar sobre ese plano para asegurarse de que no va a fallar, cómo lograr que se equivoquen menos los programadores o bien lograr que el software se desarrolle más rápido.
Otra diferencia sustancial es que en la ingeniería civil las fórmulas de cálculo estructural que se usan para determinar si la estructura de un edificio es la correcta están sólidamente entendidas y, si están bien utilizadas. se puede probar que un edificio no se va a caer. En cambio, esas fórmulas no existen en la ingeniería de software, que depende de la naturaleza discreta del código de programación. En este sentido, matemáticamente es imposible garantizar de antemano que un software no tendrá errores en un tamaño razonable, aseverar que el programa no se va a “colgar” (que un programa se cuelgue significa que no se detenga cuando debería) o que el código de programación responderá a la funcionalidad del programa. Este tema fue graficado con el “problema de la parada” por el pionero de la computación Alan Turing, en 1936 cuando aún no había computadoras. El célebre matemático inglés demostró que no es posible escribir un programa de computadora que nos diga si otro programa cualquiera se queda o no se queda colgado. En suma, este propósito no se puede lograr de manera automática.
Puede fallar
A lo largo de las últimas décadas, se han producido importantes errores de software. Ejemplos hay muchos, desde un caso curioso como la desaparición temporal de Suecia del mapa de Internet por la falla de un archivo de procesamiento, hasta errores de programación en las misiones espaciales (Fobos 1, Mariner 1 y Ariane 5) y de vulnerabilidad con el caso “Heartbleed”, que develó en 2014 un grave error en el método de encriptación OpenSSL, que había comprometido durante años la seguridad de dos tercios de las páginas web existentes. Incluso uno de estos errores se ha cobrado vidas humanas, como la falla de software en el equipo de radioterapia canadiense Therac-25, que entre 1985 y 1987 hizo que se suministraran sobredosis de radiación causando la muerte de seis pacientes.
Un estudio de la Universidad de Cambridge estima que en todo el mundo el 50% del tiempo del desarrollo de software se destina a localización y corrección de errores. Se calcula que esto impacta en la economía global en más de 300 mil millones de dólares anuales.
Pero, al fin y al cabo, ¿por qué se producen errores de software? Una primera aproximación a este interrogante consistiría en entender que programar resulta una actividad intelectual sumamente compleja.
“Posiblemente la humanidad no construya otros productos con la complejidad que tiene el software, donde un pequeño error produce una discontinuidad completa: el programa deja de funcionar”, señala Sebastián Uchitel, investigador en ingeniería de software y director del Instituto UBA-CONICET en Ciencias de la Computación.
Uchitel codirige el Laboratorio de Fundamentos y Herramientas para la Ingeniería de Software (LAFHIS), junto a Víctor Braberman, en Exactas-UBA, donde se investigan y se desarrollan técnicas automatizadas para respaldar el análisis de productos de software, incluyendo requerimientos, diseño y código.
Conversando sobre la etimología de la palabra “software”, el investigador comenta que el término no resulta del todo representativo de cómo es un programa en la práctica, “da la idea de que algo ‘soft’ es blando y, por lo tanto, maleable. Y que, si está arruinado en un lugar, no importa. Cuando en realidad el software sería como un vidrio, enorme, donde un bug o error de sintaxis resulta en una imperfección que produce una rajadura que cruza todo el vidrio y puede hacer que se desplome”.
Es evidente que la juventud de la ingeniería de software, comentada anteriormente, complejiza de alguna forma la disciplina, más allá de sus fundamentos teóricos, que no cambiarán. “Estamos recién empezando a identificar tipos de software que requieren diferentes tipos de técnicas. Cuando habíamos entendido cómo funcionaba esta ingeniería, se inventó la Web y luego empezaron los sistemas móviles e Internet de las cosas”, reflexiona Uchitel.
El investigador comenta que, en las décadas de 1970 y 1980, había una clasificación estable: software crítico y de soporte. Del primero dependen vidas humanas o la misión crítica de una organización, por ejemplo, una empresa que fabrica caños de acero, y su maquinaria está controlada por software que hace que el horno funcione a cierta temperatura o se muevan piezas y que, si funciona mal, la fábrica no produce. En cambio si se detiene por un día un programa que maneja la emisión de facturas, es grave pero solucionable. Otra distinción posible es entre software reactivo o interactivo: es distinto el software más tradicional al que se le pide calcular algo y da una respuesta automática, como si fuera una calculadora, del software que se está ejecutando todo el tiempo y uno pregunta, responde y hay interacción continua, como sucede con las aplicaciones actuales del teléfono.
Cada uno de estos sistemas tiene un conocido algoritmo (conjunto ordenado de operaciones sistemáticas), pero se trata de características generales que no abarcan nuevos problemas de la ingeniería de software. Esto puede apreciarse con los sistemas de software móvil: no es lo mismo un sistema reactivo que funciona en un teléfono celular, que tiene problemas de batería y que se mueve de un lado al otro, que un sistema reactivo en una computadora, con el que el usuario se conecta regularmente a Internet y luego se desconecta.
“Cuando hay un error concreto de diseño o implementación recae del lado del desarrollador. Tiene que mejorar su software. En cambio, cuando surge una mala comprensión del requerimiento, hace falta que el desarrollador interactúe con el cliente, porque la especificación es compleja”, destaca Nazareno Aguirre, Investigador del CONICET y Profesor asociado del Departamento de Computación de la Universidad Nacional de Río Cuarto (UNRC).
Aguirre, quien se especializa en métodos formales de desarrollo, explica que la ingeniería de software puede apuntar a mejorar procesos o productos. Ambos son caminos vinculados y complementarios entre sí.
En el caso de los procesos, se analizan las etapas y elementos a trabajar en el trayecto de desarrollo (captura de información, formalización de requisitos, planificación y diseño e implementación del software) que garanticen que se alcanzará un estándar de calidad, un software más confiable y que tenga menos desvíos entre el comportamiento esperado y el comportamiento real del sistema.
En cambio, con los productos, se utilizan métodos de testeo (testing), que efectúan un análisis del producto de software para lograr un resultado más eficiente. Una herramienta eficaz para esta tarea es el compilador, que se ocupa de la traducción de código para buscar inconsistencias en las variables de funcionalidad en el programa. Y cuando encuentra esos errores, genera la tarea de depuración (debugging) en los errores del producto de software.
Soluciones de calidad
Con el propósito de encontrar soluciones que permitan minimizar los errores de software y aumentar la confiabilidad y calidad del producto, los investigadores desarrollan técnicas eficientes e innovadoras.
Una rama en la que se trabaja arduamente es la programación automática, que consiste en lograr que un programa comprenda y automatice la tarea de programar. Estas técnicas de razonamiento automático llevan al investigador a testear numerosas veces ese programa hasta tener cierta confianza de que funciona correctamente. “El problema de esa rama de síntesis de código es que debemos proveerle al programa los requerimientos de manera muy clara y específica. Esos requerimientos no pueden tener ningún error, porque el programa va a hacer exactamente lo que se le pide”, afirma Uchitel.
Desde el laboratorio LAFHIS de Exactas-UBA, están desarrollando novedosas herramientas de síntesis de controladores para construir software automáticamente a través de modelos de comportamiento. “Trabajamos en sintetizar código que usan los drones, por ejemplo, para una tarea de agricultura para la cual no vienen previamente configurados”, puntualiza Uchitel. Diseñar una estrategia en la que el dron recorra sistemáticamente el terreno y encuentre con eficiencia todos los lugares donde hay exceso de humedad en el campo, es uno de los enormes desafíos de los ingenieros de software del laboratorio. Este modelado de comportamiento, que provee la síntesis de controladores, permite al equipo de ingenieros detectar errores en etapas tempranas de desarrollo.
“La cantidad de ingenio humano que ponemos a la solución difícilmente pueda ser copiada por una computadora. Sin embargo, buena parte de la tarea de depuración de errores está siendo puramente mecánica y puede funcionar bien”, agrega Aguirre. En este aspecto uno de los avances del área de investigación en ingeniería de software de la UNRC, ha sido el desarrollo de técnicas automáticas para la generación de casos de test de software y la reducción en el conjunto de veces que el software se prueba, manteniendo la calidad del producto final. “Definimos un criterio que permite reducir el tamaño de casos de test automático, lo cual reduce significativamente tiempos y costos de desarrollo. Para establecer este conjunto óptimo de casos de test, miramos la especificación de la tarea que efectivamente debe realizar el software”, enfatiza Aguirre.
Para la industria, el panorama no es diferente y se implementan procesos de test del software sumamente estrictos para garantizar la calidad del producto. Una de las áreas principales de trabajo es el desarrollo guiado por pruebas de software (test driven development) que considera al testeo con el mismo rigor que al código de programación.
“Utilizamos herramientas de CI (continuous integration) donde la idea es integrar la ejecución de los test en diferentes puntos del desarrollo del producto de software”, destaca Guido de Caso, arquitecto de software de Medallia Argentina y doctor en Ciencias de la Computación de Exactas-UBA.
En Medallia, planifican la ejecución de prueba del producto de software en un ciclo de tres etapas: una nueva característica del programa (feature branch con test unitarios), la integración de esa característica al producto (test de integración) y un producto candidato para lanzarse como versión definitiva a menos que aparezcan errores que lo impidan (release candidate con baterías extensivas de test).
Antes de lanzar una nueva versión del software, la misma atraviesa numerosos test de seguridad, performance, regresión y numerosas pruebas que abarcan aspectos de calidad de ese lanzamiento. También el producto de software cuenta con certificaciones de calidad (por ejemplo la norma ISO/IEC 25000) y validaciones de seguridad y confidencialidad. En el caso de productos destinados a banca y finanzas, salud y gobierno, se realizan estrictas certificaciones de confidencialidad de los datos de los usuarios.
“La clave es tratar de entender cuál es el costo de un error, cuál es el costo de garantizar la ausencia de un error y cuál es el balance que se hace entre esas dos cosas. En algunos casos el costo de quitar todos los errores del software puede ser la diferencia entre que un producto salga al mercado o no”, concluye de Caso.
Hacia un software inteligente
A modo de reflexión final, más allá de que se han desarrollado novedosas técnicas de razonamiento automático del software, es importante comprender que a veces las computadoras suelen comportarse como “terminales bobas” que no interpretan al programador ni al contexto. Hacen exactamente lo que dice el programa y no lo que intentó decir el programador, y no pueden compensar los errores sintácticos o semánticos que se cometieron al escribir el programa. Todo esto a diferencia de un humano, quien puede razonar decodificando con inteligencia un texto, aunque esté mal escrito o tenga errores, y puede entenderlo utilizando más información del contexto (ya lo dijo mejor Turing: si una computadora pudiera pensar, ¿cómo podríamos darnos cuenta?).
Si bien la ciencia llegó a la conclusión de que la computadora no puede ser un analizador universal, es decir que no puede haber programas genéricos que resuelvan todos los problemas con la misma fórmula, esto no significa que no haya analizadores particulares que se concentren en ciertas áreas de los programas. Claramente, la sofisticación de las herramientas actuales muestra el crecimiento y éxito de los productos de software, especialmente en el ámbito de la industria tecnológica argentina. Es más, se trata de un aspecto de investigación avanzada de los últimos años, que ha eliminado muchas fallas en productos de software, que de haber fallado habrían causado graves problemas ya que, paradójicamente, en ciertas actividades y procesos industriales el ser humano depende cada vez más de los programas informáticos.