lunes, 19 de octubre de 2009

Resolución de problemas según Steve jobs

Es muy interesante lo que dice el señor Jobs porque es lo que nos pasa en cada nuevo proyecto. Al principio pensamos en que sabemos de qué se trata y de que la solución en la que estamos pensando es el "santo grial" por lo simple y práctica, y al ahondar en el problema nos damos cuenta de que ni es el santo grial, ni de que el problema era tan simple, ni de que la solución era tan práctica. La moraleja es: primero, no quedarnos con las primeras ideas, segundo, profundizar en el análisis del problema antes de elegir una solución, tercero, la solución será tan complicada como el problema que estemos resolviendo.

"...when you start looking at a problem and it seems really simple with all these simple solutions, you don't really understand the complexity of the problem. And your solutions are way too oversimplified, and they don't work. Then you get into the problem, and you see it's really complicated. And then you come up with all these convoluted solutions. That's sort of the middle, and that's where most people stop, and the solutions tend to work for awhile. But the really great person will keep on going and find, sort of, the key, underlying principle of the problem. And come up with a beautiful elegant solution that works."

sábado, 21 de febrero de 2009

El proceso de desarrollo de software

Este es el primer artículo del blog y quería tratar el tema más básico de la ingeniería de software: ¿cómo se fabrica el software?. En este artículo se describe de forma general como es el proceso de desarrollo de software y abre las puertas para discutir en profundidad distintas técnicas que se pueden utilizar para facilitar el desarrollo.

Un proceso es una serie de pasos, que en este caso se necesitan ejecutar para generar un producto de software a partir de determinados recursos (humanos, tiempo, dinero, etc).

A grandes rasgos, un proceso de desarrollo de software consiste de las siguientes etapas:
  • Análisis
  • Diseño
  • Implementación
  • Pruebas
  • Producción
En las cuales, la salida de cada etapa es entrada de la siguiente. El producto generado como salida de una etapa puede tener distintas formas, por ejemplo pueden ser documentos, diagramas, código fuente, etc. El tiempo y otros recursos que se le asignan a cada etapa dependerá del tipo de proyecto que se esté realizando, tal vez un sistema con poca funcionalidad pero donde hay que resolver un problema complejo, las etapas de análisis y diseño sean las más largas, mientras que si se tiene mucha funcionalidad pero más simple, tal vez las etapas de implementación y pruebas sean más largas. Obviamente esto no se puede generalizar para todos los tipos distintos de proyectos.

Objetivos de cada etapa del proceso de desarrollo de software:

Analisis:
En esta etapa lo más importante es entender cual es el problema, no el cómo será resuelto. Esta idea es necesario tenerla bien clara, ya que en general los informáticos tendemos a plantear soluciones antes de comprender en profundidad los problemas o necesidades de nuestros clientes. La salida de esta etapa serán un conjunto de documentos que explican cual es el problema que se quiere resolver, es básicamente especificar lo que el cliente necesita o quiere, por eso una de las salidas más importantes de esta etapa es la "Especificación de requerimientos" (otros nombres a esto pueden ser "Análisis de requerimientos", "Definición de requerimientos"), también se utiliza la palabra "requisito" en lugar de "requerimiento". La idea es que esta especificación sea lo que el cliente pide pero en un lenguaje más formal para que no haya ambigüedades. La ambigüedad en las especificaciones son un factor de pérdida de tiempo en los proyectos, sobre todo cuando quienes leen estas especificaciones no están en contacto con quienes las crearon.

En la "Especificación de Requerimientos" se plasman las funcionalidades que deberá tener el sistema que se vaya a construir, esto es necesario para cuando se planteen las opciones de posibles soluciones a los problemas planteados (podríamos decir que: funcionalidad = problema a resolver). Aunque hablamos de funcionalidad, hay otra clase de requerimientos del cliente que pueden no tener que ver con la funcionalidad del sistema, por ejemplo: ante una consulta, el sistema no puede demorar más de 1 segundo en responder, a esto le llamamos "requerimientos no funcionales", por que no es sobre que el sistema ofrezca la funcionalidad de consulta, es sobre el tiempo que esta tarda, lo que se considera un requerimiento de "Tiempos de respuesta". Algunos ejemplos de requerimientos no funcionales pueden ser: "facilidad de uso", "seguridad", "estabilidad", etc.

En futuros artículos mostraremos las distintas formas de especificar requerimientos, y más sobre requerimientos no funcionales.

Diseño:
Si bien en la etapa anterior se especificó el "qué" quiero resolver, en la etapa de diseño se debe especificar el "cómo lo quiero resolver. Por eso hacíamos incapié en el no tentarse en plantear soluciones en la etapa de análisis, ya que eso es tarea de la etapa de diseño.

En esta etapa se toman las especificaciones de requerimientos producidas en la etapa anterior, a partir de las cuales se puede comenzar a plantear alternativas de cómo resolver cada problema planteado en dichas especificaciones. En general el abordaje de esta etapa se hace de lo más grande a lo más chico, del caso más general al caso más particular, y existen varias técnicas para realizarlo. Por suerte existen las "buenas prácticas" en diseño de software, y podríamos decir que muchas de las buenas soluciones están basadas en estas prácticas, también conocidas como "patrones de diseño", que no son más que el planteo de un problema genérico (que incluye varios problemas particulares, por eso lo podemos aplicar al problema que estemos resolviendo) y plantea una solución genérica, que es considerada la mejor solución para ese tipo de problema. Existe abundante literatura sobre patrones de diseño. Algunos de los sitios sobre patrones:
La salida esperada de esta etapa es un documento de diseño donde se especifiquen las soluciones de los problemas que se deben resolver, en un lenguaje formal/técnico, con diagramas UML para ayudar a la comprensión de las soluciones, por ejemplo:
En otros artículos discutiremos más sobre técnicas de diseño, patrones y UML.

Implementación:
En esta etapa el objeto más importante a construir es el código fuente de nuestra aplicación. Para esto, es necesario tener todos los problemas resueltos, o sea, el diseño de las funcionalidades que van a ser implementadas. Con un buen diseño, la tarea de implementación es mucho más fluida, y el implementador se ocupa solo de resolver problemas de implementación, y no otros problemas (por ejemplo: si se necesita una funcionalidad que no fue diseñana).

Existen muchas técnicas y buenas prácticas a seguir en la implementación, pero la elección de la técnica correcta dependerá de las características del proyecto y la tecnología con la que se trabaje. La tecnología (a veces) es un determinante de cómo se hacen las cosas. Además la tecnología va de la mano del tipo de proyecto que se está llevando a cabo, aunque muchas empresas utilizan la tecnología que conocen para implementar todos los proyectos que les llegan ( me viene a la mente un dicho "Para quien solo tiene un martillo, todos los problemas parecerán clavos").

En el futuro publicaré artículos sobre algunas técnicas y prácticas de implementación.


Pruebas:
En esta etapa sometemos a nuestro "Frankenstein" a las más duras pruebas, para ver si lo que construímos en la etapa anterior (el código fuente de nuestro sistema) funciona como debería. Cuando decimos "funciona como debería", el "como debería" está definido en la "especificación de requerimientos" que hicimos en la primer etapa. Es bueno que entre la etapa de Análisis y Diseño se cree un "Plan de pruebas" que diga lo que se va a probar y como, así cuando llegamos a la etapa de pruebas, éstas se guían mediante ese plan.

La salida de esta etapa es la lista de pruebas que se realizaron y su resultado: pasó la prueba / no pasó la prueba. De modo que para esos casos que no pasaron la prueba (por ejemplo, un resultado dió distinto al que se esperaba) deben ser corregidos y probados de nuevo. Este bucle de prueba-corrección-prueba se debería repetir hasta que todas las pruebas den como resultado "pasó la prueba". Así que deberíamos terminar con código fuente donde el 100% de nuestras pruebas fueron pasadas, pero ¿quiere decir esto que nuestro sistema no tiene nungún error?, la respuesta es simple: NO. Esto se debe a que puede haber una funcionalidad que no hayamos probado que contenga errores, o simplemente se nos olvidó probar un caso y justo un trozo de código, que tenía un error, no fue ejecutado.

En futuros artículos veremos algunas técnicas para realizar pruebas y para armar un plan de pruebas.

Producción:
Luego de que nuestro sistema pasa las pruebas, se pone en producción. Esto quiere decir que se pone a funcionar donde debería funcionar, por ejemplo si nuestro cliente es una empresa y desarrollamos un sistema contable, es muy probable que haya que instalar el sistema en la empresa, y comience así a ser utilizado por usuarios reales (los contadores). Pero esta etapa no es un simple "instalo el software y me voy", en general hay muchos problemas que resolver: la configuración del equipo de producción es distinta a la del equipo de desarrollo, falta instalar el software de base (sobre el que funciona el nuestro), faltan librerías, hay que entrenar a los usuarios, el sistema puede fallar, etc, etc, etc.

Luego de la puesta en producción viene el mantenimiento del software, que es la etapa más larga del "ciclo de vida del software", pero está fuera del proceso de desarrollo. En el mantenimiento se corrigen errores que se encuentren, se agrega funcionalidad con la cual el sistema no contaba y el cliente de pronto necesite, etc.