El mal software es caro, es lento, es inseguro, es peligroso para la salud (Therac-25) y para colmo, ¡huele mal!.
Desde los orígenes de la Ingeniería del Software los problemas se han mantenido relativamente constantes a pesar de los diversos avances: los proyectos no se ajustan a los plazos, no se ajustan al presupuesto inicial, la calidad del software no se ajusta a unos estándares, el código se hace inmantenible a medida que el proyecto evoluciona, etc.
¿Existen estándares para evaluar la calidad del software?
En principio la calidad del software no tiene una metodología estándar donde se pueda certificar. Son los procedimientos para desarrollar ese software los que se certifican y los que se pueden normalizar. La normativa ISO 9000, CMMI, Moprosoft, SW-CMM son algunos de ejemplos.
Es imposible hacer una medición correcta ya que las métricas deberían medir posibles situaciones como el número de errores que se generan durante un periodo de tiempo, las líneas de código, la velocidad de ejecución, la facilidad para modificar el código o añadir nuevas funcionalidades. Algunas de estas no implican una mejora en la calidad del código como por ejemplo que un programa tenga menos líneas de código no implica que tenga más calidad tanto en cuanto la modificación se convierta en un problema.
La conclusión inicial es que la medición de la calidad es algo subjetivo y depende de los parámetros que se midan un software puede ser más o menos de calidad.
Sin las medidas sólidas que muestren la eficacia de los distintos métodos los equipos de desarrollo van a tener problemas a la hora de seleccionar el método más efectivo.
La industria del software necesita mejores pruebas de la efectividad de los distintos métodos de desarrollo. Esto lleva a punto final en el que se necesita estandarizar las métricas del software. La presencia de puntuaciones de métricas incompatibles sin ningún ratio de conversión es en sí misma un ejemplo de innovación negativa ya que no aporta beneficio sino todo lo contrario.
¿Es posible hacer un software de calidad?
Aquí habría que matizar que un software de calidad no es sólo aquel que cumple con las funcionalidades que se esperan de él, ni si su velocidad de ejecución es más o menos rápida, ni de su usabilidad, etc. Un mal software puede cumplir con todas las características anteriores y aún así no ser de calidad.
La calidad del software va a depender en gran medida de su diseño. Que un software tenga un buen diseño va a influir principalmente en su desarrollo y en todos los agentes vinculados a él. A los usuarios finales no les va a importar si ese software está en un mismo archivo gigantesco, si está modularizado o si su desarrollo sigue algún tipo de patrón. En cambio, que un software no tenga un buen diseño si que tiene un impacto directo en su desarrollo pudiendo llegar a retrasar significativamente las nuevas versiones así como la resolución de bugs.
Esto nos lleva a que un buen diseño software va a depender de su código fuente y para que un código fuente sea bueno tiene que tener unas características entre las que cabe destacar:
Estándar de codificación: que todo el código fuente presente la misma codificación va a mejorar la reabilidad del código.
Código semántico: es fundamental que el código fuente sea lo más semántico posible de manera que lo haga más descriptivo haciéndolo más entendible y evitando confusiones.
Modularización: la modularización del código fuente permite separar el código en distintos archivos facilitando juntar código relacionado los mismos archivos. Esto también tiene la ventaja de poder reutilizar el código en otros proyectos o incluir en tu proyecto código de otros.
Documentación: otra parte fundamental para tener una base de conocimiento del código fuente que ayude a comprender el porqué de cómo está diseñado el software explicando las partes más complejas.
KISS (Keep Is Simple, Stupid): mantener las cosas simples debería ser una prioridad evitando todo tipo de complejidad. Cuanto más complicado sea un software más complicado será su desarrollo.
DRY (Don’t repeat yourself): este principio trata de evitar la duplicación de código. Esta relacionado con la modularización ya que gracias a esta se puede extraer parte del código y utilizarlo evitando posibles inconsistencias.
Patrones de diseño: los patrones de diseño son soluciones a distintos problemas que estan consideradas entre las mejores prácticas.
SOLID (Single responsibility, Open-closed, Liskov substitution, Interface segregation and Dependency inversion): son 5 principios básicos de la programación orientada a objetos y su diseño los cuales tratan de evitar que el programador tenga que refactorizar el código.
¿Refactoqué?
La refactorización es una técnica para la reestructuración de código alterando su estructura interna pero sin cambiar su comportamiento externo. Se utiliza principalmente para para hacerlo más fácil de entender y más barato de modificar, sin modificar su comportamiento observable (Martin Fowler).
La única manera de asegurar que no se cambia su comportamiento es mediante test. Los test son la base de esta técnica ya que si se produce algún cambio, ya sea por un bug o cambiado el comportamiento del código, los test detectan rápidamente el cambio por lo que el desarrollador podrá arreglarlo en una fase temprana del desarrollo (cuanto más temprana más barato es).
Los test unitarios son desarrollados antes que el propio código fuente para más tarde desarrollar el código que tiene que cumplir esos test. Se podría llegar a concluir que los test son los requisitos que el código fuente tiene que desarrollar. Por esta razón se crean primero los test para la funcionalidad específica, después se desarrolla el código que la cumpla y una vez que se pasen los test con éxito se puede aplicar técnicas de refactorización sin miedo a cambiar el comportamiento o a generar bugs.
¿Hay que pasar todos los test cada vez que se genera un nuevo código?
Actualmente se han desarrollado herramientas para la automatización de las pruebas unitarias o test. Esto tiene especial utilidad ya que cada cambio se puede comprobar si ha generado algún cambio en el comportamiento o algun bug. La integracion continua se basa en pasar todos los test cada vez que se desarrolla un nuevo código con lo que si hay errores le dan al desarrollador la oportunidad (u obligación más bien) de arreglarlo inmediatamente.
Esto lleva a otra innovación en el proceso de desarrollo de software que es el despliegue continuo. Cuando una pieza de código tiene realmente valor es cuando los usuarios finales la estan utilizando, cuando está desplegada. Es por esta razón por la que el nuevo código debería estar desplegado lo antes posible así que los equipos de desarrollo han ido reduciendo los ciclos de desarrollo de manera que las distintas piezas de software puedan desplegarse automáticamente en distintos entornos hasta llegar a producción.
¿Hay algún tipo de proceso estandarizado para desarrollar un buen software?
No hay un proceso estandarizado para el desarrollo de un buen software. De hecho hay innovación constante en este campo debido a continuas mejoras a la hora de tratar de crear un mejor software.
Uno de los primeros procesos para el desarrollo de software fue el modelo en cascada siendo sus principales actividades:
Requisitos: es el qué se quiere hacer.
Diseño: es el cómo se va a hacer.
Desarrollo: es el proceso de crear el código fuente en base al diseño.
Test: las pruebas para asegurar que todo funciona correctamente.
Integración: si hubiera múltiples sistemas.
Despliegue: actividad en la que el software está disponible para el usuario.
Mantenimiento: actividad en la que se corrigen bugs, se hacen mejores tanto funcionales como de rendimiento.
Hasta que una actividad no se ha terminado no se pasa a la siguiente.
Estas fases que, inicialmente, parece que tienen lógica a la hora de desarrollar un software esconden ciertas desventajas como son que los requisitos son cerrados mientras que en los proyectos reales, y menos a dia de hoy, los requisitos están en constante evolución.
En sistemas relativamente grandes (o de cualquier tamaño realmente) desarrollar el software completamente y después probarlo es la manera más rápida de que el equipo de desarrollo entre en modo pánico al comprobar que hay cosas que no funcionan, restricciones de cualquier tipo o varios problemas que vayan surgiendo sobre la marcha. Este tipo de situaciones (bastante habitual) pueden llevar a cambiar hasta los requisitos con el consiguiente coste que conlleva.
Como comenté anteriormente el software tiene realmente valor cuando el usuario lo está utilizando. Con esta metodología todo el software tiene que estar totalmente terminado con lo que la puesta en producción se retrasa sensiblemente.
Como consecuencia de estas desventajas surgió el desarrollo incremental que dividía el proyecto en partes más pequeñas y aplicaba el modelo en cascada para cada una de ellas. Esto tenía la ventaja de poder modificar partes más pequeñas y descubrir errores en fases más tempranas.
Se empezaron a utilizar prototipos que eran versiones incompletas del software que tenían la ventaja de cubrir partes más pequeñas con lo que se podían encontrar antes los errores. También se podía involucrar al usuario durante el desarrollo obteniendo el feedback necesario facilitando la validación final del software.
A partir de estos conceptos surgió el modelo en espiral el cual está basado en el análisis de los riesgos de las distintas partes desarrollando en distintas iteraciones aquellas que conllevan un menor riesgo. En cada iteración hay 4 fases: determinar objetivos, identificar y evaluar riesgos, desarrollar y probar, planificar la siguiente interacción.
Aunque este modelo es más flexible ya que se puede incluir nuevos requerimientos requiere de personal cualificado en la identificación de riesgos y es un modelo costoso.
El modelo de desarrollo rápido de aplicaciones (RAD) surgió como necesidad de centrarse en el desarrollo más que en la planificación de las tareas. Esto conlleva la ventaja de no tener una laboriosa planificación ademas de poder modificar los requisitos. También se utilizan prototipos con lo que es un modelo flexible a la hora de realizar modificaciones en los requisitos.
Las metodologías de desarrollo ágil de software surgen como alternativa teniendo en cuenta las necesidades y cambios de requisitos donde el trabajo lo desarrollan equipos multidisciplinares (ya no hay expertos específicos) y autogestionados. Estas metodologías se minimizan los riesgos desarrollando en iteraciones cortas, entre 1 y 4 semanas, las cuales incluyen el análisis de requisitos, el diseño, la implementación, la revisión, la documentación y su puesta en producción.
Entre estas metodologías hay unas de especial mención ya que se utilizan actualmente y están bastante extendidas:
Hay otras metodologías interesantes como pueden ser Test Driven Development (TDD) donde el desarrollo está guiado por los test. Primero se desarrolla el test, se hace el código mínimo necesario para pasarlo y se refactoriza una vez pasado. La evolución de TDD llega con Behaviour Driven Development (BDD) donde se pasa de testear el funcionamiento unitario a testear comportamientos completo. Esto se hace a través de la definición de escenarios que están desarrollados en un lenguaje determinado para poder automatizar la ejecución de estos escenarios con el código desarrollado y comprobar que el código cumple las especificaciones del escenario.
Y hay más como Feature-driven development (FDD), Domain-driven design (DDD), Model-driven engineering (MDE), etc. Como se puede apreciar el desarrollo de software está en constante innovación tratando de mejorar el proceso de desarrollo de software.
Y entre todos estos puntos no hemos hablado de los distintos lenguajes de programación como pueden se C, java, C++, Objective-c, PHP, Python, Javascript, Ruby, Scala, etc. Ni de los distintos tipos de frameworks utilizados en estos lenguajes como pueden ser los MVC como Yii, Laravel, Rails, Sinatra, Django, etc. En el navegador los frameworks o librerías de Javascript como AngularJs, ember.js,Jquery, MooTools, etc. Frameworks o librerías de CSS bootstrap, LESS, YAML,etc. Frameworks de testeo unitario para cada lenguaje como JUnit, Phpunit, Jasmine, Rspec, py.test, etc. Ni tampoco hemos hablado de cómo el diseño cambia según las distintas bases de datos, de cómo están configuradas, ni de la escalabilidad del sistema como pueden ser que la aplicación esté distribuida en la nube con sistemas escalables y de alta disponibilidad.
Llegados a este punto se pueden plantear dos tipos de ingenieros de software: aquellos a los que le entran sudores al ver semejante mundo donde todo está en constante evolución y aquellos que ven la oportunidad de poder hacer aún mejor código que lo que ha estado haciendo incluso hace un rato. ¿Con cuál te quedarías? y ¿cuánto le pagarías?