Debilidades en los programas académicos de Ingeniería Informática
Hace un par de meses discutíamos con unos amigos, de quienes respeto mucho su opinión, acerca de las debilidades que notábamos en los programas académicos chilenos de las carreras relacionadas con las tecnologías de información. Alejandro Pacheco, Juan Pablo García, Juan Pablo Schmiede y yo estábamos todos de acuerdo en que las universidades no están proporcionando la base necesaria para aquellos profesionales que han de ingresar al mercado a dar soluciones de software a problemas de negocio de las empresas.
¿Cuál es el problema?
Después de dar clases, charlas y seminarios tanto a profesores como a alumnos regulares de diversas instituciones nacionales, tradicionales y no tradicionales, así como al trabajar con alumnos de esas mismas instituciones en proyectos de la vida real, mi opinión acerca del tema de este post es la siguiente:
Existe una falta notable de capacidad de abstracción en la mayoría de los profesionales. Duela a quien le duela, esa es una realidad.
Muchos creerían que las deficiencias en verdad son otras, como el desconocimiento práctico de las principales tecnologías vigentes por ejemplo. Aunque estoy de acuerdo en que ese ha sido un problema hasta hace unos 2 años atrás por lo menos, pienso que las instituciones lo han detectado y han cambiado sus programas. Hoy veo en todas las universidades e institutos ramos y cursos que incorporan y estimulan el uso de frameworks de desarrollo como Java EE y .NET, que son los más comunes de encontrar en el mercado. Hace un par de años muchos aún no contemplaban siquiera con sus alumnos el desarrollo de aplicaciones web. Hoy eso ya ha cambiado. Ya construyen aplicaciones Web con JSP, ASP.NET o PHP.
Cuando voy a visitar a diversas instituciones para dar charlas normalmente auspiciadas por Microsoft, al indagar a los asistentes acerca de su propia experiencia con lenguajes de programación, la mayoría contesta que se sienten más cómodos con lenguajes como C#, Java o Visual Basic .NET. Ya no es como antes cuando los alumnos solo conocían C y Pascal.
Aunque es bueno lo anterior, eso resuelve apenas una parte del problema. Con conocimientos prácticos de las tecnologías antes mencionadas lo único que se asegura es que estos profesionales contarán con trabajos de programadores cuando salgan al mercado laboral. Esto en realidad no es tan malo ya que es precisamente el tipo de trabajo más requerido en las empresas. Sin embargo, la clase de programadores que son estos jóvenes recién egresados es tremendamente dependiente de la guía de gente más experimentada. El lector pensará que esto es algo normal en cualquier profesión. Es verdad. No obstante, la clase de dependencia a la que me refiero no es únicamente la que tiene que ver con la práctica, como cuando se desconoce detalles de configuración de un servidor de aplicaciones determinado o de una base de datos en particular. Eso es absolutamente natural que ocurra, inclusive con profesionales que llevamos más tiempo al vernos enfrentados a una nueva tecnología.
Más bien me refiero a la deficiencia que tienen estos profesionales de cinco o seis años académicos (Juan Pablo Schmiede en broma dice que aún los de cinco o seis años de práctica) de diseñar correctamente lo que deben construir.
¿Qué es un diseño correcto?
Con la evolución natural de la ingeniería de software, hoy ya existe una serie de prácticas y convenciones consideradas estándares de facto. Por ejemplo, hoy no hay ninguna discusión seria acerca de si usar o no la Orientación a Objetos como un método de abstracción para el análisis y la construcción de software. Por otra parte, también ya hay convenciones respecto de las capas de software que debe tener toda aplicación así como los patrones de diseño a aplicar en cada una de estas capas.
Hay patrones que corresponden a la capa que ha de contener la lógica de acceso a datos (DAO, ORM, Gateway entre otros), patrones que guían cómo organizar la lógica del negocio (Layer Supertype, Active Record, Transaction Script entre otros) así como patrones aplicables a la lógica de presentación (MVC Front Controller, MVC Page Controller, MVP entre otros). Para sistemas más complejos, que involucren probablemente varias aplicaciones que comparten mucha lógica de negocio así como el acceso a información externa, y que además sean de uso crítico demandando gran cantidad de recursos corporativos para poder cumplir con ciertos niveles acordados de servicio, también existen patrones ya probados que guían en cuanto a cómo distribuir físicamente tales aplicaciones con el fin de lograr buena disponibilidad sin perder gubernabilidad (Remote Façade, Business Delegate, Proxy entre otros). Hay patrones que orientan en cuanto a cómo organizar tales comunicaciones y hasta cómo soportar transacciones (atómicas o por compensación) en contextos distribuidos (SOA).
En resumen quiero llegar a esto: un buen diseño significa que, ante un determinado requerimiento de negocio, de nivel de servicio, y obviamente según el contexto, se pueda determinar a priori las partes que deberá tener la solución. Y junto con la determinación de las partes, es vital que se identifiquen las responsabilidades de cada parte. El diseño debe mostrar claramente estas distinciones. Y la construcción del software debe reflejarlas.
Lo anterior es válido para cada uno de los niveles de abstracción de un sistema. Desde el nivel atómico cuando definimos las estructuras de datos con las que se trabajarán dentro del proyecto (e. g. Cliente, Cuenta, Póliza, Producto, Linea, Contrato, etc.), pasando luego por las capas en las que se dividirá el sistema, seguido por los componentes o servicios que se vayan a necesitar, hasta el nivel de procesos y de interfaces gráficas requeridas por las aplicaciones. Cada uno de los anteriores es un nivel de abstracción distinto dentro de una jerarquía que va de lo más específico a lo más general, de lo más técnico a lo más funcional. Y hay patrones de diseño también que aplican a cada uno de estos niveles.
Para el diseño de clases, la manera como estas deben relacionarse e interactuar, están los 23 Patrones Fundamentales conocidos como “GOF” (Patrones Creacionales, Estructurales y Comportacionales) publicados por Erich Gamma, Richard Helm, Ralph Johnson y John Vlissides.
Respecto de cómo implementar lo anterior en código, independientemente del lenguaje, están los aproximadamente 100 Patrones de Implementación publicados por Kent Beck en su libro “Implementation Patterns”. En él Kent Beck identifica patrones que aplican a la construcción de Clases, de Atributos, de Comportamiento, de Métodos, de Colecciones y de Frameworks.
De la misma manera están los 51 Patrones Arquitecturales publicados por Martin Fowler en su libro “Patterns of Enterprise Application Architecture” (PEAA) que aplican al nivel de capas de una aplicación: Patrones para la Lógica de Dominio, para la Lógica de Acceso a Datos, de Mapeo de Modelos Relacionales a Modelos Orientados a Objetos, de Presentación Web, de Distribución, de Concurrencia Offline, de Manejo de Sesiones y otros. Complementariamente, Martin Fowler en su sitio ha seguido identificando y publicando patrones arquitecturales relacionados con el manejo de información Temporal, Eventos, Contabilidad y Presentación.
En un Paper publicado por el Open Group Security Forum y escrito por Bob Blakley y Craig Heath, se describen los 13 Patrones Fundamentales relacionados con la Seguridad de Sistemas (Patrones de Disponibilidad y Patrones de Protección).
Y no puedo dejar de mencionar a los casi 70 Patrones de Tests Unitarios identificados y publicados recientemente por Gerard Meszaros en su libro “xUnit Test Patterns”. Meszaros magistralmente organiza estos patrones de acuerdo a las 4 Fases que debe poseer todo Test Unitario: Preparación del Escenario de Prueba (Fixture), Invocación del Sistema Bajo Prueba (Exercising the SUT), Verificación de los Resultados (Result Verification) y la Destrucción del Escenario de Prueba (Teardown). Desde luego que espero poder escribir en más profundidad en un futuro próximo acerca de estos patrones así como también de los Hedores de los que habla Gerard en su libro: Code Smells, Behavior Smells y Project Smells. Sin embargo por el momento quiero apenas mencionar que, dada la actual experiencia con la que contamos en la Ingeniería de Software, ya no es posible hablar de un Desarrollo Decente de un proyecto si no están presentes los Tests Unitarios. Y estos Tests Unitarios deben existir desde el principio de la programación si es que no antes (si pensamos en Test-Driven Development y en Extremme Programming) y deben ser construidos por los mismos programadores.
La deuda académica
Volviendo a nuestro tema principal, en mi opinión un ingeniero informático debería ser formado primeramente en esta visión de las partes de un software. Luego debería llegar a conocer cuáles son las tecnologías actualmente vigentes que implementan los principales patrones de diseño antes mencionados. Y por último aprendería a como aplicar los patrones de diseño estudiados con las herramientas y tecnologías de su predilección.
Una metodología de enseñanza que abordara el tema de la ingeniería informática de esta manera, desde una perspectiva Top-Down, es la que creo que mejor prepararía a un profesional del desarrollo de software. A este profesional se le haría muchísimo más simple entender una nueva tecnología desde la mirada de los patrones.
Lo anterior es debido a que hoy en dia toda nueva tecnología que sale al mercado no es más que una nueva implementación de un Patrón de Diseño ya probado en la comunidad. Por ejemplo, tecnologías nuevas del mundo Microsoft como LINQ, Entity Framework, los Enterprise Library Application Blocks, Unity, Windows Communication Foundation, Windows Presentation Foundation o ASP.NET MVC son todas implementaciones de Patrones conocidos tales como: Object Relational Mapper, Abstract Factory, Dependency Inyection, Model-View-Presenter, Remote Façade, Proxy, entre muchos otros. En el mundo Java las tecnologías equivalentes que implementan los mismos patrones son Spring, Hibernate, iBatis, Session Beans, Entity Classes, JSF, XUI, Tango, entre otros. Y en el emergente mundo Ruby encontramos Ruby On Rails en conjunto con una serie de otras herramientas que, una vez más, implementan los mismos Patrones de Diseño antes mencionados.
Si nuestros alumnos aprendieran primero los Patrones de Diseño y luego conocieran las tecnologías que los implementan, o ellos mismos crearan nuevas implementaciones en diferentes lenguajes y plataformas, todo sería muy diferente. Los estaríamos preparando para que el dia de mañana fueran capaces por sí mismos de aprender rápidamente cualesquier nuevas tecnologías aunque no las hayan visto en la universidad.
La capacidad de determinar cuáles son las partes a crear de una aplicación junto con sus respectivas responsabilidades, en cada uno de los niveles de abstracción del software, en función de un escenario dado, es precisamente lo que gran parte de los profesionales informáticos hoy les cuesta mucho lograr. Y las universidades en general es poco lo que están haciendo (desde luego que unas más que otras) por revertir esta situación. Pero el hecho de que tú hayas leido estas pocas palabras ya da indicio de que existe esperanza.