La importancia de la ingeniería de software

[English version]Este enlace se abrirá en una ventana nueva

Introducción

En posts anteriores os he hablado de la importancia de la adaptabilidad y el mantenimiento. En este post me gustaría centrarme en los aspectos relacionados con la ingeniería existente bajo este proceso, en concreto del punto de vista de la ingeniería de software.

9378193_339189378193_33918

En primer lugar, me gusta pensar que los productos software que construimos no son sino una serie de sistemas y componentes interrelacionados, podríamos discutir sobre el lenguaje de programación o si usamos funciones u objetos. Pero, en realidad, eso no es excesivamente importante. El factor crítico es que cada uno de ellos debería interpretarse como una unidad de funcionamiento. La verdad es que tanto el concepto de sistema como el de unidad de funcionamiento están discutidos hasta la saciedad en toda la literatura existente sobre ingeniería de software. No obstante, me gusta destacar esta aproximación en lugar de hablar de APIS o servicios puesto que, por una parte, me ayuda a pensar en cómo se deberían comportar cada uno de los componentes software y, por otra parte, la idea de sistema, sus dependencias, su capacidad de ser modelado y simulado se alinean con mi educación en otro campo de la ingeniería.

En cualquier caso, este concepto de unidad es importantísimo. Imaginemos una máquina compleja compuesta de varios componentes o incluso subsistemas. No sé, pongamos, por ejemplo, que nuestra máquina consta de varios sistemas de engranajes y un motor eléctrico. Lógicamente, lo que queremos es que el sistema en su conjunto funcione bien, lo cual necesariamente supone que cada una de las piezas también lo haga e incluso que los engranajes, pudieran ser utilizados en máquinas distintas a las que estoy evaluando, respetando, así, una serie de reglas y especificaciones. Cuando hablamos de software, hablamos de los mismos conceptos e ideas, seguimos una serie de estrategias de diseño que nos permitan la adecuada reutilización de los sistemas o componentes desarrollados.


Trasladando el concepto

El problema se origina cuando intentamos explicar como los sistemas de software deben construirse, como debemos aislar cada uno de los componentes o como podemos conseguir la reutilización de los mismos. La verdad es que no es fácil, adquirir una completa compresión de este problema requiere que el instructor cuente con el conocimiento preciso y que el alumno tenga la madurez suficiente como para adquirir los conceptos.

11107581_463006211107581_4630062

No podemos negar que hemos desarrollado patrones, principios de modelado como SOLID y herramientas como TDD que tratan, precisamente, de ofrecer un camino a seguir para el desarrollo de sistemas. Pero, ¿un desarrollador no suficientemente maduro es capaz de entender lo que todo esto puede ofrecerle?. Desde mi punto de vista, no sin una adecuada mentorización, trasladando lo que es correcto y lo que no y apostando por una enseñanza en base a ejemplos.

Un caso concreto

A fin de clarificar mi punto de vista, me gustaría céntrame en un caso concreto, un subsistema en el que estamos trabajando en este momento. En particular, un Bot para Whatsapp y Telegram, en realidad nada demasiado complejo puesto que es un sistema de preguntas dirigidas. La dificultad no viene, por tanto, de lo qué es sino de la adecuada descomposición del sistema y el testeo de cada una de las partes involucradas. Imaginemos por un momento que somos desarrolladores junior y nos tenemos que enfrentar a este problema. ¿cómo lo haríamos? Podríamos tener la suerte de que nuestro jefe de equipo nos sugiriera empezar por Telegram para luego continuar con la implementación de Whatsapp. Desafortunadamente, desde el punto de vista del diseño del sistema esta recomendación sería un desastre. ¿Por qué? Pues porque, como os he comentado, somos desarrolladores junior y, probablemente, seguiríamos una aproximación similar a la siguiente:

  • Buscaríamos una librería que implementará el API de Telegram en nuestro lenguaje de programación favorito.
  • Tras localizar la librería nos pondríamos a desarrollar para, muy probablemente acabar, con un bloque switch gigantesco en el que nos limitaríamos a analizar peticiones y procesar respuestas, fuertemente acoplado a la librería que hemos seleccionado en el primer paso.
  • Puesto que tengo que hacer tests, lo más seguro es que tratará de probar todo el sistema en su conjunto, olvidando casos de frontera que estarían ocultos en la complejidad de mi implementación.

Si somos suficientemente senior seguramente estaremos inquietos ante la idea de semejante "código" puesto que, entre otros problemas, esa estrategia conduciría a una duplicidad de código y una reimplementación de Whatsapp desde cero. El problema no es sólo del junior, sino que también se basa en una deficiente comunicación de objetivos o seguimiento por parte del líder técnico o del jefe de proyecto. Una comunicación más efectiva, desde el punto de vista de la arquitectura del sistema, debería enfocarse en trasladar la necesidad de construir los siguientes bloques:

Imagen2Imagen2

En concreto,

  • El sistema de conversación que sería responsable tanto de recibir preguntas como emitir respuestas de forma independiente al concepto del Bot. Este sistema debería mantener internamente conceptos tales como el estado, las posibles interacciones y acciones, etc.
  • La capa de red que se utilizaría para intercambiar información con el proveedor de Bots (Whatsapp o Telegram). Esta capa de red podría ser una librería de terceros.
  • La capa de integración que facilitaría la interconexión entre el subsistema de conversación y la capa de red, trasladando los mensajes de red a las entradas esperadas y convirtiendo las respuestas del subsistema de conversación a mensajes de red.
  • Como no puede ser de otra manera, el testeo debería hacerse en cada capa, y si hablamos de tests unitarios, deberíamos escribirlos de tal forma que sólo testeemos el código específico de cada capa.

En conclusión

La verdad es que podríamos pensar que un sistema software construido de esta manera es complejo. Bueno, la verdad, es que lo es. Más aun si consideramos que cada uno de los componentes individuales deberían ser escritos teniendo en cuenta ideas similares, incluyendo aislamiento o diseño basado en comportamiento. Las ventajas pudieran parecer sutiles pero en realidad son importantes.

  • El sistema será adaptable, puesto que podemos mejorar una de las capas sin afectar al resto. Si tenemos un nuevo tipo de mensaje o un nuevo tipo de cliente de bots (más allá de Telegram o Whatsapp) sólo tenemos que añadir una nueva capa de red o un nuevo componente del subsistema de conversación.
  • El sistema se podrá mantener puesto que la descomposición del mismo en componentes, facilita la modificación del código y entender su objetivo.

Por otra parte, cualquier sistema tiene un cierto grado de complejidad. Si volvemos a la analogía del sistema de engranajes, saber lo que hace uno de ellos no quiere decir que sepamos crear un reloj automático. Ignorar la complejidad de este tipo de trabajo, la ingeniería que lo sustenta sólo conduce a sistemas inmantenibles y a costes mayores que los esperados inicialmente.