Carta abierta a un Profesor de mi Universidad

Necesitaba poner un dibujo, y sólo encontré este. Créditos: http://jc-schools.net

Introducción

No sé si lo he mencionado, pero estoy involucrado en un movimiento en la Facultad que busca la redefinición de nuestro programa de Ingeniería de Sistemas y establecer tres programas diferenciados: Ingeniería de Sistemas, Sistemas de Información e Ingeniería de Software. Uno de nuestros colaboradores publicó el manifiesto en uno de los grupos Facebook de la facultad, y un profesor de reconocida trayectoría y prestigio acotó lo siguiente:

¿Qué es y qué ámbito de aplicación sostienen los que proponen desarrollar la Ingeniería de Software? Los que laboran haciendo software, que yo sepa, sólo hacen software aplicativo y de negocio. No software de base, ni software de desarrollo, ni software de desarrollo asistido, ni software instrumental, ni software complejo. Y el profesional de Sistemas de Información diseña, construye, dirige, supervisa y evalúa software aplicativo. ¿Para hacer software aplicativo se necesita una carrera profesional?
He hecho algunas correcciones de estilo. Publiqué la respuesta en el mismo grupo, pero dado que resulto un tanto extensa quisiera que quedara publicada en el Blog:

La Carta

Buenas noches Profesor, yo me considero un practioner de la Ingeniería de Software y con mucho gusto daré respuesta a sus inquietudes. Primero, déjeme contarle que llevo 7 años en el rubro y que a la fecha sólo he laborado en empresas con sede en el Perú, por lo que mi experiencia le a a ser útil para que se haga una idea del futuro de esta disciplina dentro de nuestro mercado.

Quisiera comentarle también que en el año 2010, mientras trabajaba para una Consultora costarricence con sede en Perú, junto a un equipo de Ingenieros peruanos le dimos mantenimiento al aplicativo CRM de una empresa líder del rubro de Buscadores de Internet: Se trataba del Software que le daba soporte a todas las operaciones de venta de esta compañía a nivel mundial, con equipos distribuidos a lo largo del planeta para darle soporte a este aplicativo vital. Mi equipo -de 7 personas- le daba soporte a el sólo Módulo de Administración, por lo que puede hacerse una idea de la complejidad de este Producto Software. Como ese caso podría darle al menos dos mas de mi experiencia personal, pero al punto que quiero llegar es que se produce software altamente complejo aquí en Perú, y la disciplina académica encargarda de lidiar con la complejidad de este tipo de sistemas -porque sí, son sistemas- es la Ingeniería de Software.

Por otro lado, creo que tiene un error conceptual respecto a lo que es Sistemas de Información, al menos desde la perspectiva de ACM-AIS. La disciplina de Sistemas de Información -que no es una disciplina de Ingeniería- tiene por objetivo ayudar a una compañía u organización a lograr sus objetivos mediante tecnologías de información. Le pongo un ejemplo con el que he estado familiarizado: Las empresas de telecomunicaciones tienen una área de Sistemas, así como tienen un área de Recursos Humanos o de Finanzas.  El área de Sistemas cuenta con profesionales de Sistemas de Información, que determinan que para abaratar costos -un objetivo organizacional- deberían tener un Portal de Autoatención, y son estos profesionales los que realizan una especificación de requisitos  inicial y un diseño con las principales funcionalidades. Asimismo, los profesionales de Sistemas de Información determinan que lo mejor para el negocio -ellos siempre piensan en la empresa- es tercerizar este desarrollo, por lo que le envían el requerimiento a una Consultora, digamos, de la India. El personal de la Consultora que diseña, construye, dirige, supervisa y evalúa la construcción de este aplicativo son profesionales en  Ingeniería de Software y no de Sistemas de Información. La disciplina de Sistemas de Información está orientada a la organización y empresa, por lo que típicamente se encuentra es Escuelas de Negocios y no es común que sean los principales responsables de la consecución de un Proyecto Software.

Finalmente, no porque sea un “software aplicativo” deja de ser complejo o de importancia. Por ejemplo, en una empresa de telecomunicaciones americana hemos visto un Software de Tickets de Atención -un dominio aparentemente sencillo- que está desplegado en dos Datacenters en distintas partes del globo, con versión Escritorio, Web y  una de Respaldo que es mantenido por equipos de desarrollo desplegados en Estados Unidos, Argentina, India y Perú. Si este aplicativo deja de funcionar, la empresa pierde millones, y la Ingeniería de Software es la que garantiza que todo funciona como debe: Temas como Gestión de Proyectos, Gestión de la Configuración, Calidad de Software y Arquitectura y Diseño son nuestro día a día. Y esto pasa acá en Perú, y muchos egresados de la UNI ya están desempeñándose en ese rubro: Los conozco y sé que son muchos.


Quisiera terminar respondiendo su última pregunta. La ACM Computing Curricula establece que sí existe un Programa de Pregrado orientado a la construcción de Software Complejo -sin importar si es aplicativo, software base u otra tipología- y ese Programa es Ingeniería de Software. En ese mismo documento se establece un profesional en Ingeniería de Software puede ser un licenciado en Ciencias de la Computación que ha tenido cursos de Ingeniería de Software en su Plan de Estudios o un Bachiller en Ingeniería de Software. Creo firmemente la facultad -y la UNI- deben optar por la segunda vía, en concordancia con nuestra tradición como Escuela de Ingenieros.

Quiero hacer Computación y no sé qué estudiar

Ellos estudian Computación. Créditos: University of Toronto

Cómo he contado en otro post, allá por el año 2001 había decidido que hacer Software era mi vocación y debía obtener un grado universitario que me permitiera hacerlo. En caso de quedarme en mi natal Trujillo, hubiera podido acceder a los siguientes programas:
  • Informática, en la Universidad Nacional de Trujillo.
  • Ingeniería de Sistemas, en la Universidad de Trujillo o en la Universidad César Vallejo.
  • Ingeniería de Computación y Sistemas, en la Universidad Privada Antenor Orrego.
  • Ingeniería de Sistemas Computacionales, en la Universidad Privada del Norte.
Como ven, bastante nombres pomposos y -según iba escuchando en las ferias vocacionales- casi todas estas carreras de nombres disímiles tenían contenidos curriculares similares. Ahora, cuando comencé a evaluar las universidades limeñas -causándole una pena a mi madre- me encontré con las siguientes opciones:
  • Ingeniería de Sistemas, en la Universidad Nacional de Ingeniería o en la Universidad Nacional Mayor de San Marcos.
  • Ingeniería Informática en la Pontificia Universidad Católica del Perú.
Para mis ojos ingenuos de postulante tenía un listado largo de nombres que a la larga eran lo mismo.  Como también he narrado en otro Post, al final opté por seguir Ingeniería de Sistemas en la UNI; seguí el programa disciplinadamente y cuando me disponía a buscar prácticas profesionales noté lo siguiente:
  • Las empresas necesitaban Ingenieros de Sistemas para desempeñarse como Analistas de Negocios, determinando las necesidades tecnológicas de las empresas.
  • Las empresas necesitaban Ingenieros de Sistemas para administrar Servidores de Bases de Datos, Servidores Web e incluso infraestructura de redes.
  • Las empresas requerían  de Ingenieros de Sistemas para construir Software, ya sea Programadores para construir el Software, Testers para probarlo o Gerentes de Proyecto para administrar el proceso.
El haberme graduado de Ingeniero de Sistemas en la UNI me ha permitido hacer carrera en el mundo del desarrollo del Software. Sin embargo, muchos de mis compañeros han optado por rumbos distintos -incluso tengo dos compañeros que han incursionado en el Marketing- por lo que aparentemente ser Ingeniero de Sistemas en el Perú te da la posibilidad de desempeñarse en cualquier rubro. Y no creo que esto sea positivo para la profesión.

Este desorden en lo referente a programas, currículas y ámbito profesional es un fenómeno eminente latinoamericano. De primera mano sé que esto también pasa en Colombia. Sin embargo naciones que están más alejadas del tercer mundo -como Brasil- tienen la formación en Computación normada, con contenidos definidos y ámbitos de ejercicio plenamente determinados. El objetivo de este Post, es mostrarles cómo es la formación en Computación de clase mundial, para reconocer nuestra problemática y empezar a trabajar en corregirla.

Primero, empecemos por quien pone las reglas en lo referente a Computación: las sociedades profesionales más importantes son las IEEE Computer Society y la Association for Computing Machinery. Estas dos junto a sociedades adicionales se reúnen periódicamente y emiten un documento denominado Computing Curricula, que contiene los programas de Computación actualmente disponibles y con estándares curriculares definidos (que no les sorprenda no encontrar a la Ingeniería de Sistemas). En la versión 2005 de este documento, comienzan lógicamente definiendo Computación:
Computación es cualquier actividad -orientada a objetivos-  que requiera de computadoras, se benefecien con el uso de computadoras o busque crear computadoras. (Traducción libre)
Aquí sería bueno dejar en claro que Computación es a efectos prácticos equivalente a Informática, y utilizaremos ese término a lo largo de este post y de todo el Blog. Como ven, es una visión bastante amplia, y por eso en el reporte se señala que es virtualmente imposible obtener maestría en todas sus disciplinas, razón por la cuál se ha dividido en cinco programas, cada uno con propósito y contenidos específicos:

Ingeniería de Computación

Este programa emergió de los programas de Ingeniería Eléctrica, y se encarga del diseño y construcción de Computadoras o sistemas basados en Computadoras. Para esto, la gente de Ingeniería de Computación utiliza prácticas de Ingeniería Eléctrica y Matemáticas, teniendo más énfasis en Hardware que en el Software.

Un Ingeniero en Computación está en capacidad de diseñar y construir teléfonos celulares, reproductores de música digitales o cámaras de video digitales. No creo que haya un programa universitario en el Perú orientado hacia este perfil.

Ciencia de la Computación

Esta es una disciplina de computación de amplio alcance. La Computing Curricula le define tres campos de acción: el diseño e implementación de software, el descubrimiento de nuevos usos para las Computadoras (por ejemplo, el desarrollo de Internet) y el desarrollo de formas efectivas de resolución de problemas de computación (como la forma más eficiente de almacenar la información en una Base de Datos).

Esta es la disciplina de Computación más difundida a nivel mundial, y es bastante común que -por ejemplo- empresas de alta tecnología como Google, Microsoft o Facebook están en demanda ese tipo de profesionales. Actualmente en el país están emergiendo programas de Ciencia de la Computación, ahora me viene a la mente la UNI y la UPC en Lima y la UNSA y la UCSP en Arequipa.

Sistemas de Información

Este es otro programa antiguo y popular, centrado en integrar tecnologías de información con los procesos de negocio de modo que los negocios y las empresas satisfagan sus necesidades de información y logren sus objetivos. Pueden inferir que esta disciplina sirve de puente entre la comunidad técnica y la comunidad de administración en una organización.

Típicamente, los programas de Sistemas de Información se ubican dentro de Escuelas de Negocios y el plan de estudios contiene cursos tanto de negocios como de Computación. Es una disciplina bastante necesaria y practicada en nuestro medio, puedo decir que varios compañeros de Universidad se desempeñan en ese rubro. Además, en el país ya tenemos dos programas acreditados, en la UPC y la USMP.

Tecnologías de Información

Es el complemento de Sistemas de Información en lo referente a Computación para negocios, y se ocupa de satisfacer las necesidades de tecnología para todo tipo de organización. Mientras que Sistemas de Información se centraba en la información, los profesionales de tecnologías de información hacen énfasis en la tecnología en sí misma por lo que entre sus responsabilidades está la administración de redes de computadores, de sistemas de correo electrónico y de servidores Web.

A pesar de ser una disciplina tan necesaria, y en la que se desempeña una gran cantidad de egresados de Ingeniería de Sistemas en el Perú, no sé de ningún programa universitario que esté tomando esa orientación. Tal vez los Institutos deberían buscar la acreditación en este rubro.

Ingeniería de Software

Este programa emergió de los programas de Ciencia de la Computación, ante la creciente complejidad e importancia del Software. La Ingeniería de Software se encarga del desarrollo y mantenimiento de software de manera confiable y eficiente, de modo que el software sea de desarrollo y mantenimiento asequibles y satisfaga todos los requerimientos definidos por sus usuarios.  La Ingeniería de Software es una de las dos disciplinas de Ingeniería dentro de la Computing Curricula, e incluso en ABET es acreditada por el comité de Ingeniería y no por el de Computación. La formación de un Ingeniero de Software incluye Matemáticas, Ciencia de la Computación y prácticas de Ingeniería.

Internacionalmente, uno puede formarse en Ingeniería de Software llevando cursos como parte de un programa de Ciencia de la Computación o llevando un programa propio de Ingeniería de Software. Muchos Ingenieros de Sistemas peruanos -especialmente los que trabajan en consultoras- se desempeñan en este rubro y están llegando empresas del extranjero buscando gente de ese perfil. A la fecha, tenemos dos programas acreditados por ABET -la UPC y la URP- y están apareciendo nuevos programas en el País.

Ahora todo está bastante más ordenado -al menos más que en el 2001- y varias universidades del país se están dando cuenta de la necesidad de alinearse a estos estándares y no trabajar de espaldas al mundo. Sólo espero que mi Facultad también lo haga.

Programar es fácil


Programar en Java es tan sencillo que puedes aprenderlo en sólo un  día. Puedes comprar el libro aquí.

Si es que siguen el Blog deben saber que soy un egresado de Ingeniería de Sistemas de la UNI, que esta facultad y este Programa han proveído de profesionales en Computación al país por más de 30 años y que como parte del proceso de acreditación han decidido adoptar a la Ingeniería de Sistemas como el programa a acreditar y dejar de lado la Computación. Y si es que siguen el Blog, sabrán también que me opongo rotundamente a esa decisión y que estoy en franca y solitaria campaña porque mi facultad adopte la identidad que ya tiene, por más contradictorio que esto suene. Como parte de esta “cruzada personal” me he visto envuelto una serie de discusiones en Facebook, y en una de esas discusiones me sorprendió el siguiente comentario:
Con respecto a la Ingeniería de Software no dudo que seguirá cambiando el mundo, pero éste no ha sido el principal motor sino saber usar la información y/o el avance de las TIC’s. Ejemplos por doquier ¿Facebook no?. La creación de Facebook va más allá de los procesos y herramientas utilizadas por esta disciplina, pero fue indispensable sin duda.
Algunas correcciones de estilo son mías. Si bien aún se presenta diplomático, percibí entre líneas cierto desdén hacia lo relativo a Programación: Incluso pone a esta disciplina en segundo plano en un producto eminentemente tecnológico como Facebook. La respuesta en verdad me pareció increíble, y le repliqué que una gran de emprendimientos tecnológicos -léase Google, Amazon, Microsoft- tienen como líderes a gente con formación en Computación y que estar educado en esta disciplina es vital si es que se quiere innovar y tener éxito en el mercado tecnológico. Y fue aquí que obtuve esta réplica más sorprendente aún:
Le preguntaría a los desarrolladores de aplicaciones que más necesitan saber de informática o computación para crear una aplicación análoga a Facebook , Twitter, LinkedIn, etc (sic). Jóvenes con poca noción en informática - con los frameworks, creados gracias a la Informática y computación, que facilitan la creación de aplicaciones - se hacen millonarios de la noche a la mañana.
Corregí algo de sintaxis para hacerlo entendible. Aquí se dejó de diplomacia y creo entender -corríjanme si no es cierto- que tiene dos ideas bastante arraigadas respecto a la programación:
  • Programar es fácil, cualquier persona con nociones básicas puede producir aplicaciones de clase mundial como Facebook o Twitter. 
  • Asimismo, con ese conocimiento tan básico le basta y le sobra para tener éxito y hacer millones en un mercado tan competitivo como Internet. 
En su defensa, mi interlocutor afirma -no sin cierto orgullo- que no se dedica a la Ingeniería de Software sino “resuelve problemas de Sistemas en organizaciones” así que esos puntos de vista tan controversiales pueden ser producto de desconocimiento. Como he dicho en ocasiones anteriores, existe cierta aversión de ciertos docentes para la Ingeniería de Software en mi facultad y esta opinión puede haber llegado al estudiantado. Es por esto, que en este post quiero defender dos ideas:

La construcción de Software es compleja

Algo que nos parece tan evidente para los que nos dedicamos a la programación no lo es tanto para el resto, incluso para gente que trabaja junto a nosotros como Jefes de Proyecto o Analistas. En un brillante artículo, Peter Norvig estima que te puede tomar 10 años el dominar el arte de construir software, como se demuestra experimentalmente con una amplia cantidad de disciplinas.

Es por eso que me sorprende ver en el mercado local ver a colegas ostentando el título de Arquitecto sin haber llegado a esta cuota mínima de experiencia: Eso no ocurre en otras latitudes. Norvig también brinda una receta para convertirse en un programador experto, de la que mencionaré algunos puntos que me parecieron importantes:
  • Se requiere primero programar, y programar mucho. No basta con leer tutoriales en la Web o libros donde dicen enseñarte a programar en 24 horas. 
  • Ir a la Universidad (opcional) 
  • Construir programas junto a otros programadores. Esto incluye ser el mejor para aprender habilidades de liderazgo o ser de los peores para aprender de los mejores. 
  • Mantener programas construidos por otros programadores, comprender código ajeno y diseñar programas de modo que sean de fácil entendimiento por otros programadores. 
  • Conocer a las computadoras, dado que es el entorno donde se ejecutan los programas. Esto no es conocer Windows, sino saber cómo es que nuestro programa será ejecutado en el Procesador, el manejo de caché y políticas de paginación a bajo nivel. 
Norvig consigna algunos ítems más, con las cuáles llegaremos a dominar nuestro oficio luego de dedicarle 10 años. Y mientras aprendemos iremos seguramente produciremos mucho software mediocre pero que se hará mejor cada día que pase: Pensar que podemos construir una solución escalable y de clase mundial con sólo algunos años de entrenamiento es bastante ingenuo.

Contar con talento técnico es clave para toda Startup

Hace unos días estuvo en Lima Antoinette del Río, que es gerente de juguetes y juegos en Amazon. Al ser consultada sobre los déficits en innovación que tiene el Perú, dió esta lapidaria respuesta:
Principalmente pasar de la creatividad a la acción. Los peruanos siempre hablamos de nuestra creatividad, de cómo siempre tenemos solución para todo. Trato de seguir de cerca la movida de los start-ups en Perú y veo algunos esfuerzos pero, honestamente, siento que la ejecución aún está débil. En tecnología, no hay cupo para fallar, si alguna vez te llama la atención una página, entras, no carga o lo que ves no es lo que esperas y listo, no vuelves, hay un solo momento de la verdad. Mi esperanza está en que las carreras técnicas cobren valor, y que pronto toda esa creatividad pueda concretarse en start-ups inteligentes y de calidad que realmente mejoren la vida de la gente.
Lamentablemente en nuestro medio esto no es evidente, mientras que en Estados Unidos el Santo Grial el emprendimiento es encontrar al “Technical Cofounder”, aquella persona que esté en capacidad de transformar una idea en un producto.

Vengo de leer un artículo en Forbes de Stella Fayman, del que quiero rescatar algunos puntos:
  • Ella coincide con Norvig es que la Programación es un arte que lleva años aprender, así que esta parte no debe ser subestimada, es decir, no creas que puedes construir un producto de valor a punta de tutoriales. 
  • Contratar programadores es difícil, y esto también se está dando aquí en Perú. Han venido varias empresas americanas y otras tantas transnacionales que están distorsionando el mercado -a beneficio de los programadores- y ahora ser programador está mejor pagado. 
  • Hay que tener cuidado con utilizar outsourcing, es decir, mandar el trabajo a la india. El construir un producto valioso requiere de interacción con tu equipo y esto se hace bastante difícil con un equipo offshore. 
Y para terminar, una cita que está en el mismo artículo:
El desarrollo de software es un arte, no es un commodity. Trátalo como tal, y tendrás éxito. (Traducción libre)

Ingeniería de Sistemas, cosa tan rara

Un meme, algo feo pero muy cierto. (Créditos: http://aynomanches.blogspot.com)


Nunca me ha sido fácil conseguir amigos, y mucho menos conseguir amigas: Soy víctima de una timidez crónica que se agudiza al estar cerca del sexo opuesto. Ahora a mis 28 años ya es menos evidente, pero cuando llegué a Lima a mis 17 años la tenía a flor de piel. El 2002 llegué a Trujillo a la casa de mi tía -y mis cinco primas- con el firme objetivo de convertirme en un estudiante de la UNI y también con unas habilidades sociales casi inexistentes. Aún el día de hoy, mis primas se burlan de aquellos días de postulante, cuando pasaba por la sala con la mochila llena de libros y las mejillas al rojo vivo cuando las encontraba junto a sus amigas y me veía forzado a saludar y por ende a socializar.

En Agosto de ese mismo año ingresé a la UNI y mi reciente estatus de estudiante de Ingeniería de Sistemas me había convertido de repente en Soporte Técnico de la casa: Esto incluía a mis tías, mis primas y por extensión a sus amigas. Muchas fueron las veces que tocaron a mi puerta alguna prima acompañada de una asustada amiga, que necesitaba con desesperación que arreglara la Computadora de la casa antes que lleguen sus papás. Fue así que mi reducido círculo de amigas -que inicialmente sólo incluía a mis cinco primas- se fue expandiendo poco a poco, y mi timidez se iba diluyendo con ello (aunque nunca del todo).

Y déjenme decirles que era bastante bueno, en gran medida a que junto a mis hermanos estropeamos la computadora de la casa de Trujillo una infinidad de veces y siempre era yo el que tenía que arreglar el estropicio para evitar la ira de papá. Fue ese entrenamiento el que me permitía reparar una Computadora y no la educación que me estaba impartiendo la Universidad. Sin embargo, para mis primas y sus amigas mis habilidades venían de que me estaba formando como Ingeniero de Sistemas, y que lo estaba haciendo en la UNI.

Pues resulta que Ingeniería de Sistemas no tiene nada que ver con darle soporte técnico a computadores, muy a pesar de lo que piensa mi familia y una cantidad importante de la población. Según INCOSE, referente mundial en Ingeniería de Sistemas, un ingeniero de Sistemas se encarga de lo siguiente:

“Un Ingeniero de Sistemas líder debe dirigir a ingenieros de las más diversas ramas, e incluso a médicos, abogados y psicólogos de modo que satisfagan los requerimientos del proyecto sin importar la naturaleza de estos. Un profesional en Ingeniería de Sistemas es el pegamento que mantiene al proyecto unido” (Tradución libre)
La labor del Ingeniero de Sistema está estrechamente vinculada al manejo de la complejidad, por lo que las principales instituciones que promueven -léase el Departamento de Defensa de los Estados Unidos o la NASA- llevan a cabo proyectos de magnitud y costo significativo que requieren de la convergencia de especialistas de las más diversas ramas. Por ejemplo, el proyecto Apollo requirió de Ingenieros Mecánicos, Ingenieros Eléctricos e Ingenieros de Software trabajando coordinadamente para lograr el objetivo de llevar al hombre a la Luna. Es ahí donde la Ingeniería de Sistemas se hace necesaria, y es por eso que los Ingenieros de Sistemas son uno de los profesionales más reconocidos y mejor pagados a nivel mundial.

Asimismo, la Ingeniería de Sistemas es rara vez practicada por Ingenieros de Sistemas. Bueno, para expresarme mejor, por gente que tenga un Bachillerato en Ingeniería de Sistemas. Los profesionales en Ingeniería de Sistemas en su gran mayoría tienen formación en alguna otra Ingeniería y han hecho un Postgrado en Ingeniería de Sistemas. Por ejemplo, les presento a Ralf Hartmann, director de estrategia de INCOSE y Vice-Presidente de Estrategia en Astrium, una fabricante de equipos y vehículos espaciales. Ralph es Ingeniero Eléctrico de la Universidad de Karlsruhe, y como él, muchos practicantes de la Ingeniería de Sistemas vienen desde otras disciplinas.

Esto se debe en parte a que existen corrientes de pensamiento que postulan que para poder tener una visión amplia de múltiples ramas de la Ingeniería primero se debe tener una visión profunda de alguna de sus disciplinas. A la fecha, los programas de Pregrado en Ingeniería de Sistemas son muy pocos habiendo por el contrario una gran oferta de programas de posgrado, cosa que no ocurre con el resto de disciplinas de la Ingeniería. Además, no existe en la actualidad una estructura curricular de referencia para programas de pregrado existiendo por el contrario ya publicada una para ofertas de posgrado.

Como ven, la Ingeniería de Sistemas tiene poca relación con formatear computadoras e instalar Windows; cosa que he venido evangelizando en mi familia y con cualquier persona que quiera escucharme. Fue esa Ingeniería de Sistemas la que se creó en la UNI el año 1974, bajo la dirección del Ingeniero Augusto Mellado, siendo el primer programa de Ingeniería de Sistemas del país. Si ustedes ven la currícula de Ingeniería de Sistemas de aquel entonces, verían que si bien tiene algunos cursos de computación explora y abarca las más diversas áreas de la Ingeniería. Un Ingeniero de Sistemas de aquellos años tenía formación en resistencia de materiales, termodinámica y mecánica del cuerpo rígido, por mencionar algunos de los nombres que más me asustaron.

Sin embargo, y tal vez debido a mi limitado círculo social, actualmente no conozco a ningún Ingeniero de Sistemas que esté ejerciendo la disciplina que pone transbordadores en el espacio o cazas de combate en los cielos. Yo actualmente hago Ingeniería de Software, como muchos de compañeros, mientras que otro buen grupo está actualmente dedicado a Sistemas de Información. Como he contado anteriormente, yo ingresé a la UNI con la intención de hacer Computación, mis padres esperaban que hiciera Computación y cuando terminé el programa descubrí que el mercado esperaba que como Ingeniero de Sistemas hiciera Computación, porque la gente la UNI es reconocida en ese campo.

En el Informe de Evaluación Curricular, elaboran una hipótesis sobre esta confusión que ya lleva más de 30 años. En el informe se señala que en los años 80, cuando estaban saliendo las primeras promociones de Ingenieros de Sistemas, los egresados incursionaron en el ámbito profesional y nunca regresaron al ámbito académico. Es decir, los especialistas en Ingeniería de Sistemas no retornaron a las aulas y los espacios académicos fueron cubiertos por gente de Computación que fueron degenerando a la profesión en lo que ahora es.

Si bien ese escenario puede ser posible, yo tengo otra hipótesis: La demanda de Ingenieros de Sistemas en el Perú es ínfima, mientras que la demanda de profesionales en computación fue siempre creciente desde los años 70. Y cómo en aquellos años no existían profesionales ni programas de Computación suficientes -tenemos a UNMSM con Computación Científica y la PUCP con Ingeniería Informática- esta demanda fue suplida por los Ingenieros de Sistemas, que bien o mal por lo holístico de su formación tenían nociones de Computación.

Y ahora la Computación florece en el Perú: tenemos programas de Ciencias de la Computación, Sistemas de Información e Ingeniería de Software en el país; las grandes casas de Software del mundo están viniendo al Perú para quedarse y en los Concursos de Programación nuestros connacionales cada vez obtienen más galardones. Todo esto gracias a generaciones y generaciones de Ingenieros de Sistemas -y hay que decirlo, también otros profesionales- que descubrieron en la Computación una pasión y una forma de vida.

Actualmente la UNI y mi facultad están en pleno proceso de acreditación, y luego de un intenso debate han optado por acreditarse como Ingeniería de Sistemas en lugar de optar por un programa de Computación. Me preocupa enormemente que esto signifique dejar de lado la Computación y tirar por la borda años de trabajo y prestigio y adoptar un programa que actualmente no tiene demanda en el mercado lo que puede afectar la empleabilidad de nuestros graduados. Se está planteando que los egresados vayan a desempeñarse en roles de gestión de proyectos o reorganización de procesos, que actualmente son dominio de profesiones más especializadas -como Ingeniería Industrial- donde habría que ver que tan mal o bien el mercado reciba a estos jóvenes profesionales.

Para mí, esta decisión es un error y espero tener éxito en mi intento de rectificación. O en su defecto, espero estar equivocado que sería muy triste que un programa con tanta historia y prestigio como el de Ingeniería de Sistemas fracase.

La UNI, la Ingenieria de Sistemas y yo

Chicos UNI. Entre ellos, estoy yo (Créditos: Ray Mendoza)


Allá por el año 2001 yo tenía 17 años y dos grandes preocupaciones en la vida: A quién llevar a la fiesta de promoción y qué hacer con mi vida luego de terminar el colegio. Ese año fue un gran año porque ambas cosas salieron mejor de lo que esperaba: el 2001 conseguí a mi primera enamorada y descubrí que mi vocación era ganarme la vida haciendo software.

Lamentablemente, estas aparentes soluciones trajeron más problemas de los que solucionaron: Mi enamorada de estreno era el interés romántico de uno de mis mejores amigos y tenía que escoger la universidad que me acogería por 5 años. Mi madre -y mi enamorada- querían que estudie en Trujillo, mientras que mi padre quería que me fuera a Lima. Además, el bolsillo de mi padre quería que fuera una universidad pública, con lo que varias de mis opciones iniciales fueron desechadas rápidamente.

Me encontraba en medio de esas tribulaciones, cuando llegó a casa el tío Miguel. El tío Miguel es un familiar muy querido, y lo recordaba siempre en su taller de mecánica, rodeado de máquinas enormes y guiando con temple a sus operarios: Siempre que mis hermanos y yo íbamos al taller nos hacía pasar a su oficina, que estaba llena de planos pegados en las paredes y revistas en inglés desparramadas en su escritorio. Las visitas eran siempre agradables porque nos permitía jugar con su regla de cálculo y además hacía trucos de magia que se tornaban en ciencia luego de su explicación.

En aquella visita, el tío Miguel me dejó dos cosas: Un libro de Diseño de Ingeniería y una cita de Theodore von Karman: “Los científicos estudian el mundo tal como es, los ingenieros crean el mundo que nunca ha sido”. El tío Miguel tenía en la pared de su oficina -en un lugar privilegiado- un título de Ingeniero Mecánico de la Universidad Nacional de Ingeniería: luego de esa visita había decidido que yo también quería uno igual y que sería un Ingeniero de Sistemas de la Universidad Nacional de Ingeniería.

Una serie de fracasos y mucha perseverancia fueron los que me llevaron a ingresar a la Universidad. Esos primeros días fueron maravillosos, donde caí en la cuenta que era un privilegio formar parte de un grupo humano con tanto talento y donde muchos compañeros compartían mi pasión por el software. Puse todas mis esperanzas en el curso de Introducción a Ingeniería de Sistemas esperando que me dijeran cómo esta disciplina me permitiría hacer software, sólo para que el profesor las diluya al hacernos ver que la Ingeniería de Sistemas no es una disciplina de Computación, sino un enfoque holístico hacia la resolución de problemas complejos.

Y en nombre de ese enfoque holístico mis compañeros y yo hemos visitado las más diversas disciplinas: tomamos un curso de algoritmos pero no somos gente de computación, estudiamos Microeconomía y Macroeconomía sin ser Economistas, y vimos contabilidad por tres ciclos sin ser contadores. Además, ciclo tras ciclo al menos un profesor nos venía con la cantaleta que la gente que se dedica a hacer software está condenada a la monotonía y a la mediocridad.

Pero yo estaba en la UNI por dos motivos principales: Quería ser Ingeniero -quería crear por medio de la ciencia- y quería hacer software, solucionar problemas mediante las computadoras. A pesar de lo descrito en el párrafo anterior, la UNI me dio dos espacios donde ese deseo podía germinar: el Capítulo Estudiantil de la IEEE Computer Society y el Programa de Ayudantía del Centro de Cómputo. Fue en esos dos grupos donde descubrí que la Ingeniería de Software es una disciplina reconocida en el mundo y que día a día lo está cambiando, que el poder programar es un talento raro y altamente valorado por el mercado y que con mi vocación podía hacer grandes cosas, por el país y por mi.

Llegado el momento de buscar trabajo, descubrí que no estaba sólo en mi vocación. La comunidad Programación-FIIS agrupa a una gran cantidad de egresados de la facultad que ven en la Programación una forma de vida. Fue gracias a ellos que pude desempeñarme como Programador, para caer luego en la cuenta que los egresados de la Universidad -y la facultad- somos altamente apreciados por el mercado y se reconoce nuestra calidad aún por encima de programas propios de Informática o Computación.

Hace ya más de 6 años que me desempeño profesionalmente como Ingeniero de Software y ha sido una ruta bastante gratificante. La profesión me ha permitido interactuar con gente de diversos lugares del planeta, y en empresas como Google he visto de primera mano los niveles de sofisticación que la Ingeniería de Software puede tener y su nivel de impacto en la vida de mucha gente. Fue por eso que me llenó de entusiasmo que la facultad haya intentado acreditarse como Ingeniería de Software y me llenó de tristeza ver la oposición generalizada a esta propuesta, con argumentos principalmente orientados hacia el desmedro de una disciplina que literalmente está transformando la forma en que vivimos.

Personalmente no creo que la Ingeniería de Sistemas sea nociva, es más, la considero necesaria. Pero cuestiono seriamente que se busque enseñar desde programas de pregrado por dos razones: Primero, los programas de pregrado en Ingeniería de Sistemas propiamente dicha son muy raros en el mundo, al nivel que no existe una currícula de referencia para este tipo de ofertas educativas; y segundo, veo muy difícil que jóvenes egresados puedan poner en práctica esta disciplina y que el mercado laboral actualmente tenga necesidad de estos profesionales. Una búsqueda rápida en computrabajo.com pondrá en evidencia que los especialistas en dinámica de sistemas o metodología de sistemas blandos son raramente requeridos.

La realidad es que los egresados de Ingeniería de Sistemas mayoritariamente irán a desempeñarse en dos disciplinas: O Sistemas de Información o Ingeniería de Software. Y lamentablemente, nuestros jóvenes egresados estarán en abierta desventaja contra los egresados de otras universidad que sí les ofrecieron a los estudiantes un cuerpo de conocimiento definido. Recuerdo que en mi primer trabajo como practicante en Ingeniería de Software, mi mentor era otro practicante egresado de otra universidad debido a que su currícula le había dado más experiencia en programación que yo.

Sí me preguntan cuál es el logro del que me siento más orgulloso, diría sin titubear que ingresar la UNI: muchos jóvenes peruanos quieren tener ese privilegio y somos muy pocos lo que lo hemos logrado. En segundo lugar, sería haber egresado de la UNI: los Ingenieros más talentosos que he conocido son de esa casa de estudios y nada se compara a la satisfacción de llamar “colega” al tío Miguel. Pero también me gustaría que la Universidad, y principalmente mi facultad, sean más receptivas con aquellos adolescentes que, cómo yo en su momento, llegan llenos de sueños sobre Software y Computadoras. Tengo fé que esta situación cambiará y pienso hacer todo lo que está en mis manos para que esto suceda.

Estructuras de Datos: Lista Enlazada

Si han revisado los posts anteriores, para realizar un ordenamiento de elementos nos valíamos de un arreglo para contener los elementos a ordenar. Los arreglos en Java tienen un tamaño fijo en memoria asignado al momento ser instanciado: Si nuestro arreglo fue instanciado con una longitud de 100 y sólo colocamos en él 30 elementos hay 70 espacios en memoria que están siendo desperdiciados. Por otro lado, si requerimos ordenar ahora 101 elementos el arreglo anterior no nos sirve y necesitamos instanciar uno más grande. Ahora, si queremos insertar un elemento en medio de un arreglo ya poblado tenemos que desplazar todos los elementos hasta encontrar el lugar apropiado para nuestro nuevo elemento. Si nuestro arreglo es demasiado grande tantos desplazamientos pueden hacer sufrir al CPU de la máquina donde estamos ejecutando el algoritmo.

Para evitarnos estos inconvenientes, podríamos dejar de usar los arreglos y usar una estructura de datos que no tenga un tamaño fijo y cuyo coste de inserción sea significativamente menor, como las Listas Enlazadas. De acuerdo a Data Structures and Algorithms in Java, las Listas Enlazadas se ven así:

Una Lista Enlazada está compuesta por una serie de Nodos (o Links), donde cada Nodo contiene la data almacenada y una referencia al siguiente Nodo de Lista; de este modo la Lista sólo necesita una referencia al primer Nodo para poder tener acceso a todos sus elementos. En la imagen se ve que para insertar un elemento en la primera posición de la Lista, sólo es necesario cambiar la referencia a este primer elemento para que apunte hacia el nuevo elemento; y el elemento nuevo debe apuntar hacia el ex-primer elemento como su elemento posterior.

Pero tanto palabrerío se comprende mejor en Java:


 package test;  
 public class SortedList {  
      private Link first;  
      public SortedList() {  
           first = null;  
      }  
      public boolean isEmpty() {  
           return (first == null);  
      }  
      public void insert(long key) {  
           Link newLink = new Link(key);  
           Link previous = null;  
           Link current = first;  
           while (current != null && key > current.dData) {  
                previous = current;  
                current = current.next;  
           }  
           if (previous == null) {  
                first = newLink;  
           } else {  
                previous.next = newLink;  
           }  
           newLink.next = current;  
      }  
      public Link remove() {  
           Link temp = first;  
           first = first.next;  
           return temp;  
      }  
      public void displayList() {  
           System.out.print("List (first-->last): ");  
           Link current = first;  
           while (current != null) {  
                current.displayLink();  
                current = current.next;  
           }  
           System.out.println("");  
      }  
 }  
 class Link {  
      public long dData;  
      public Link next;  
      public Link(long dd) {  
           dData = dd;  
      }  
      public void displayLink() {  
           System.out.print(dData + " ");  
      }  
 }  

Esta no es una Lista Enlazada cualquiera, tiene la peculiaridad que ingresa los nodos en orden. La clase Link representa cada Nodo de la Lista,  y cómo mencionaba tiene sólo dos atributos: la data que contiene y la referencia al siguiente Nodo de la Lista. Por otro lado, nuestra Lista Enlazada -representada en la clase SortedList- sólo tiene el atributo first, que hace referencia al primer elemento de la Lista.

En nuestra Lista los elementos son ingresados en orden mediente el método insert. Para eso hacemos lo siguiente:
  1. Creamos una nueva instancia de Link (o sea, un nuevo Nodo) con el valor a insertar.
  2. Declaramos dos variables locales: La variable current nos servirá para almacenar el Nodo sobre el que nos encontramos al recorrer la Lista mientras que previous será utilizada para almacenar el Nodo anterior al Nodo en el que nos encontramos.
  3. Empezamos el recorrido de la lista desde el primer elemento, haciendo que current haga referencia a first.
  4. Recorreremos el arreglo mientras tenga elementos (current != null) hasta que encontremos los nodos entre los que debemos insertar el nuevo elemento.
  5. Cuando hemos salido del bucle ya tenemos la ubicación del elemento a insertar, sólo nos queda reasignar los atributos next del Nodo previous (para que apunte al nuevo Nodo) y del Nodo newLink (para que referencie al Nodo current)
Podríamos utilizar esta Lista para ordenar elementos y no tener que utilizar los algoritmos de posts pasados. Analizando la eficiencia de esta estructura, vemos que es costoso agregar elementos a la lista dado que -en el peor escenario- tendríamos que comparar al nuevo elemento con todos los elementos de la Lista. Por otro lado, el tiempo que nos toma extraer el menor elemento es mínimo, constante y no depende del tamaño de la Lista dado que este elemento siempre se encontrará en  el atributo first de nuestra Lista (y lo podemos obtener mediante el método remove). 

Código fuente e imágenes tomadas de Data Structures and Algorithms in Java.

Estructuras de Datos: Cola de prioridades

Una Cola  -en el contexto de las Ciencias de la Computación- es una estructura de datos FIFO. Esto significa que los primeros elementos que fueron ingresados en la cola serán primeros en ser removidos. Además, los nuevos elementos a ingresar a lo cola serán colocados en la parte posterior de la misma, como los clientes en las colas de los Bancos.

Eso lo sé desde la Universidad.

Sin embargo, en Data Structures and Algorithms in Java  descubrí un tipo de Cola llamada Colas de Prioridades; donde los elementos la posición de los elementos en la Cola está en función a un criterio predefinido; como en las colas de las discotecas en las que las jovencitas agraciadas siempre ingresan primero, sin importar la hora en la que hallan llegado.

Les presento el código que incluye el libro, para una Cola de Prioridades donde se procura que los elementos de menor valor numérico -nuestro criterio-sean removidos primero de la cola:

 package cgc.learning;  
 public class PriorityQ {  
      private int maxSize;  
      private long[] queArray;  
      private int nItems;  
      public PriorityQ(int s) {  
           maxSize = s;  
           queArray = new long[maxSize];  
           nItems = 0;  
      }  
      public void insert(long item) {  
           int j;  
           if (nItems == 0) {  
                queArray[nItems++] = item;  
           } else {  
                for (j = nItems - 1; j >= 0; j--) {  
                     if (item > queArray[j]) {  
                          queArray[j + 1] = queArray[j];  
                     } else {  
                          break;  
                     }  
                }  
                queArray[j + 1] = item;  
                nItems++;  
           }  
      }  
      public long remove() {  
           return queArray[--nItems];  
      }  
      public long peekMin() {  
           return queArray[nItems - 1];  
      }  
      public boolean isEmpty() {  
           return (nItems == 0);  
      }  
      public boolean isFull() {  
           return (nItems == maxSize);  
      }  
 }  

Nuestra Cola de prioridades está implementada en la clase PriorityQ, que para inicializarse requiere como parámetro el máximo número de elementos que puede soportar la cola. Usaremos este número para instanciar el arreglo de longs que contendrá la data.

Iremos agregando data en el arreglo mediante el método insert, que es también el encargado de que los elementos sean insertados en orden. Se debe procurar que al registrar elementos en el arreglo, los elementos mayores se encuentren a la izquierda -de modo que el mayor elemento insertado tenga índice 0 en el arreglo- mientras que los menores sean almacenados a la derecha, lo que implica que el menor elemento insertado se encuentre en la posición nItems -1 del arreglo. En la siguiente imagen:

La flecha Rear apunta hacia el elemento de mayor valor, que tiene el índice 0 en el arreglo; mientras que la flecha Front apunta hacia el elemento de menor valor con índice nItems -1 en el arreglo de PriorityQ. Al ingresar un elemento mediante el método insert, debemos procurar que este criterio se mantenga; por lo que realizamos lo siguiente:

  • En caso el arreglo no tenga elementos, el nuevo elemento ingresado ocupará la posición 0 del arreglo. Al realizar un registro, siempre se incrementa el valor de  nItems de modo que este atributo siempre contenga el número de elementos de la Cola.
  • En caso el arreglo no esté vacío, compararemos el elemento a ingresar con cada uno de los elementos del arreglo empezando por el menor-el de índice  (nItems  -1 )- desplazando los elementos existentes hacia la derecha hasta encontrar el lugar apropiado para el nuevo elemento de modo que el arreglo se mantenga ordenado.

Con esa lógica de inserción aseguramos que al invocar a remove siempre obtendremos el menor elemento de la lista, que es el primero de la Cola. Dado que realizamos comparaciones a lo largo del arreglo al momento de insertar, el tiempo de inserción es directamente proporcional al número de elementos de la Cola; sin embargo, el método remove tiene un tiempo de ejecución constante y no depende del número de elementos de la cola.

Para finalizar, las colas de prioridades se aplican -por ejemplo- en los sistemas operativos multi-tarea. Los procesos se colocan en una Cola de Prioridades y es un función a la prioridad/importancia relativa que les da el sistema operativo que pueden acceder a recursos como el CPU.

Código fuente e imágenes tomadas de Data Structures and Algorithms in Java.



Algoritmos: Ordenamiento por inserción

Si hay algún algoritmo que recuerdo de mi época universitaria es el Ordenamiento de Burbuja: lo recuerdo porque en todos los exámenes te solicitaban ordenar y era el algoritmo más fácil de memorizar. Ayer encontré a mi querido algoritmo de la Burbuja en el capítulo de ordenamiento simple de Data Structures and Algorithms in Java, dedicado a los algoritmos más sencillos - y lentos - de ordenamiento. De los tres algoritmos del capítulo, el de la Burbuja es el más lento e ineficiente de todos; por lo que a pesar de mi nostalgia decidí dedicarle el post al Ordenamiento por Inserción: el más rápido de todos los algoritmos fáciles.

La idea del algoritmo es que a través de las iteraciones construyamos dos partes diferenciadas con los elementos que tenemos que ordenar: una parte ordenada y una parte desordenada. Conforme pasan las iteraciones, iremos agregando elementos a la parte ordenada con elementos de la parte desordenada, hasta que la parte desordenada se quede sin elementos y nuestra parte ordenada sea el resultado del algoritmo. Imaginemos que estamos aplicando el Ordenamiento por Inserción para alinear un filar de personas por orden de talla. En una iteración intermedia, tendríamos este escenario:

El algoritmo ya ha hecho su trabajo y nos ha dividido al grupo de personas en dos partes: una ordenada y una que tenemos que ordenar. Lo que se hace en cada iteración es tomar al primer elemento de la izquierda de la parte desordenada (en el gráfico "Marked player") para  colocarlo en lista ordenada en el lugar que le corresponda. Entonces, sacamos a "Marked player" de la fila para ver donde lo colocamos.


Hemos retirado a "Marked player" y esto ha dejado un  espacio libre en la lista de personas. Lo que vamos a hacer ahora es desplazar a las personas de la lista ordenada hacia la derecha (aprovechando el nuevo espacio libre) hasta que encontremos el lugar que le corresponde a "Marked player". Después de mover a la derecha a tres grandulones, encontramos el espacio que le corresponde a "Marked player". Lo  colocamos en su sitio y nuestra fila india quedaría así:

Ahora la parte de la izquierda-que estaba ordenada- ha crecido en una persona y la parte desordenada de la derecha es una persona menor. Es momento de escoger un nuevo "Marked player" tomando a la primera persona de la izquierda de la lista desordenada. Repetimos el proceso tres veces más y hemos terminado.

Si en vez de un grupo de personas utilizamos un arreglo de enteros, el programa Java para ordenarlos mediante ordenamiento por inserción sería más o menos así:

 public void insertionSort() {  
           int in, out;  
           for (out = 1; out < nElems; out++) {  
                long temp = a[out];  
                in = out;  
                while (in > 0 && a[in - 1] >= temp) {  
                     a[in] = a[in - 1];  
                     --in;  
                }  
                a[in] = temp;  
           }  
      }  

Cuyo proceso es más o menos el que sigue:

  1. La variable nElems contiene la longitud del arreglo a[] que vamos a ordenar. La variable out la utilizamos para marcar el inicio de la porción no ordenada del arreglo. Nuestra parte ordenada al incio del algoritmo será el primer elemento de a[] (o sea a[0]) y la parte desordenada comenzará en a[1]; es por eso que la variable out se inicializa en 1: esta variable nos marcará siempre el inicio de la parte desordenada del arreglo.
  2. Colocamos el valor de a[out] (nuestro "Marked player") en la variable temp, dado que al desplazar los elementos de la parte ordenada hacia la derecha podríamos perder su valor.
  3. Inicializamos la variable in con el valor de out, y utilizaremos esta variable para desplazarnos a lo largo de la parte ordenada con el fin de encontrar el lugar que le corresponde a a[out] en la parte ordenada del arreglo. Para esto, comparamos si a[i-1] es mayor que el valor de a[out] almacenado en temp. Si es así, desplazamos a[i-1] a la derecha, y disminuimos i en una unidad para seguir recorriendo la parte ordenada del arreglo.
  4. Cuando a[i-1] sea menor que temp -que contiene a[out]- es que hemos encontrado al fin el lugar que le correspode a nuestro "Marked Player". El valor de a[out] ahora estará almacenado en a[i], y estamos listos para tomar el siguiente elemento de la parte desordenada del arreglo.
Como en el post anterior, nos toca determinar cúantos pasos le toma a nuestro algoritmo ordenar un arreglo arbitrario de longitud N, en el peor escenario posible (la gente de algoritmos siempre es pesimista). En la primera iteración, la parte ordenada tiene sólo un elemento, así que a lo más realizaríamos una comparación al ordenar. En la segunda iteración, como ya tendríamos dos elementos en la parte ordenada el algoritmo realizaría como máximo dos comparaciones; y en la última iteración, sólo nos quedaría realizar N-1 comparaciones en el peor escenario posible. Si sumamos todas las comparaciones, tendríamos:

1 + 2 + 3 + ... + N - 1 = N*(N-1)/2  

*De acuerdo a la fórmula que enseñan en secundaria

El tiempo de ejecución del algoritmo es proporcional al número de pasos; por lo que el tiempo de ejecución del algoritmo de ordenamiento por inserción para un arreglo de longitud N es proporcional a N al cuadrado.

Código fuente e imágenes tomadas de Data Structures and Algorithms in Java.


Orgullo de artesano (de Software)


"The whole problem with modern times is that there's no pride in craftsmanship. (...) Everyone's a slave of efficiency! No time for aesthetics! No love of things for their own sake!" -  Calvin & Hobbes de Watterson.

La frase que abre el post la encontré hoy en un tira de Calvin & Hobbes, y podría ser traducida como "El problema de estos tiempos es que se ha perdido el orgullo del artesano. ¡ Todos son esclavos de la eficiencia! ¡Ya no hay tiempo para la estética! No hay amor  para las cosas en sí mismas". Mientras todos los niños hacen bolas de nieve acumulándola entre las manos, Calvin se asegura que las suyas tengan la cantidad óptima de humedad y un diseño aerodinámico.

¿No creen que la frase de Calvin aplica también al mundo del desarrollo de software? Al Jefe de proyecto sólo le importa que el software esté listo a tiempo, al usuario sólo le preocupa que el software funcione y a los ingenieros sólo les interesa cobrar puntualmente. Eficiencia y productividad son los valores máximos de la industria de software hoy.

Creo que el software y su código fuente tienen un componente estético. Sí, yo creo que los programas pueden ser "bonitos".  Revisar código fuente ordenado, indentado, con nombres de variables y métodos adecuados puede llegar a emocionarme tanto como un buen cuento o una canción. Los programas con tipos desacoplados, capas definidas, estructuras de herencia elegantes y aplicación inteligente de patrones de diseño a  mi parecer pueden llegar al estatus de obras de arte.

Pero parece que la mayoría de ingenieros han perdido esos valores. Simplemente se limitan a digitar el código que haga que el software cumpla con los requerimientos del usuario. Si el código fuente es un caos lleno de nombres de variables initeligibles, clases de cientos de líneas de código y lógica de negocio dispersa pues es una lástima, pero no es su problema. Ya los ingenieros de mantenimiento arreglarán eso después del pase a producción.

Cuando codifico, trato que lo que estoy viendo en el monitor sea agradable para mi y para el próximo ingeniero que lo tendrá cargo. Trato siempre de aplicar las pocas técnicas que conozco para que el programa que estoy construyendo sea funcional, eficiente y , si se puede, elegante. Puede llevarme un poco más de tiempo y esfuerzo, pero creo que como profesional le debo calidad a la empresa que me ha contratado y a mis colegas que tendrán que lidiar con lo que he hecho.

Hace poco me enteré del movimiento de Artesanía de Software, que propone entre otras cosas considerar al proceso de desarrollo de software como un arte en sí mismo. El movimiento tiene un manifiesto con unos valores que a mi parecer todos los programadores deberíamos adoptar:


  • No sólo hacemos software que funciona, sino software bien hecho
  • No sólo respondemos al cambio, sino agregamos valor constantemente.
  • No sólo somos individuos e interactuamos, sino somos una comunidad de profesionales.
  • No sólo colaboramos con nuestros clientes, sino establecemos una sociedad productiva con ellos.
*Traducción libre

En la historieta, las bolas de nieve aerodinámicas de Calvin toman demasiado tiempo en hacerse. Para cuando tiene una lista, Susy ya lo ha acribillado con decenes de bolas de nieve convencionales. Mientras Calvin yace acribillado sobre la nieve, Hobbes sólo atina a decir:

"Artists always suffer" (Los artistas siempre sufren)

Algoritmos: Búsqueda dicotómica

Stoimen's Web Log desde hace un tiempo publica cada semana un post sobre Algoritmos. Habiendo llevado el curso de Algoritmos y Estructuras de Datos en la universidad -y habiéndolo aprobado con una nota decorosa- pensé en seguir los posts como una manera de refrescar mis algo oxidados conocimientos de algoritmia.


Ya van cinco algoritmos publicados y no he visto ninguno en la Universidad, así que parece que el profesor nos engañó al hacernos creer que sabíamos algoritmos. Me he propuesto remediar ese error, y he comenzado a leer Data Structures and Algorithms in Java para aprender lo que debí haber aprendido en la universidad. Esta serie de posts son una recopilación de conceptos producto de mi lectura.


Dicho esto, comenzamos:

¿Cómo localizarías un elemento en un arreglo ordenado? Una aproximación ingenua sería recorrer el arreglo desde la posición inicial (En Java, el índice 0) e ir recorriendo el arreglo elemento por elemento hasta localizar el elemento que estábamos buscando. Si asumimos el peor escenario posible -es decir, el elemento que buscábamos se encuentra al final del arreglo- este tipo de búsqueda requeriría N "pasos" par un arreglo de longitud N.

El enfoque anterior podría funcionar también para un arreglo desordenado, pero tratándose de un arreglo ordenado es ineficiente dado que no aprovechamos el hecho de tener el arreglo ordenado para reducir el número de pasos que le toma al algoritmo encontrar el elemento buscado. La búsqueda dicotómica nos permite aprovechar este atributo de nuestro arreglo para que -en el peor de los casos- la búsqueda tome sólamente lb (N) pasos (esto es el logaritmo en base 2 de N). Pongamos el algoritmo a prueba buscando un número X en un arreglo A de longitud 100 (N=100):

  1. Tomemos el elemento que se encuentra exactamente al medio del arreglo (A[50]) y verificamos si es el elemento que buscábamos. 
  2. Imaginemos que no lo es y que X es mayor que A[50]. Cómo se trata de un arreglo ordenado, ahora podemos afirmar con seguridad que X no se encuentre en A[49] ni en ninguno de los elementos que le preceden, por lo que sólo deberíamos buscar en los elementos entre A[51] y A[100]
  3. Tenemos ahora un nuevo arreglo en el que buscar, y según nuestro algoritmo debemos verificar si el elemento ubicado en la mitad del arreglo -o sea A[75] -es el elemento que buscábamos.
  4. Volvamos a pretender que no lo es,  pero que ahora X es menor que A[75]. Como este arreglo también se encuentra ordenado, concluimos que todos los elementos desde A[76] hasta A[100] son mayores que X, por lo que sólo deberíamos buscar entre A[51] hasta A[74]
  5. Y así hasta que encontremos el número que buscábamos. 

Si tanta palabrería la pasamos a Java, obtenemos algo como esto:

      public int find(long searchKey) {  
           int lowerBound = 0;  
           int upperBound = nElems - 1;  
           int curIn;  
           while (true) {  
                curIn = (lowerBound + upperBound) / 2;  
                if (a[curIn] == searchKey) {  
                     return curIn;  
                } else if (lowerBound > upperBound) {  
                     return nElems;  
                } else {  
                     if (a[curIn] < searchKey) {  
                          lowerBound = curIn + 1;  
                     } else {  
                          upperBound = curIn - 1;  
                     }  
                }  
           }  
      }  
Ese método pertenece a una clase donde a[] es un atributo que representa el arreglo ordenado sobre el que buscamos y nElems es un atributo que contiene la longitud del arreglo. El método devuelve la posición en a[] en la que se encuentra searchKey, y en caso no la encuentre devuelve nElems que es la longitud del arreglo. La variable curIn representa el índice del elemento que se encuentra en el medio del arreglo que estamos evaluando, siendo lowerBound el índice del menor elemento del arreglo y upperBound  el índice del mayor elemento del arreglo. En cada iteración del bucle while se compara si a[curIn] es mayor o menor que el elemento buscado searchKey y en base al resultado de esta comparación se recalculan los valores de  upperBound  y lowerBound. Con dibujitos, sería algo así:

El número de "pasos" que necesasitamos para encontrar un elemento en un arreglo de longitud N viene a estar dado por la cantidad de veces que podemos dividir N sobre 2 (antes que el producto de esta división sea menor que 1). Este número en matemáticas es igual a lb(N), que es considerablemente menor que N de nuestro enfoque ingenuo del primer párrafo: si buscamos secuencialmente un arreglo de 100 elementos nos tomaría 100 pasos, mientras que si usamos la búsqueda dicotómica nos tomaría sólo 7 (aproximadamente el valor de lb(100)).

Código fuente e imágenes tomadas de Data Structures and Algorithms in Java.

Arquitectura Java EE y Spring Framework

Hace unos meses -en mi anterior trabajo- realizé una presentación al equipo sobre fundamentos de arquitectura y su relación con Spring Framework. La presentación es fundamentalmente teórica así que van a encontrar poco código;  y consideré compartirla dado que estamos en campaña de resucitar el blog xD.

Saludos, y hasta otra!

GWT para novatos

GWT - Una introducción
View more presentations from Carlos Gavidia.

Me encargaron en el trabajo realizar una charla introductoria sobre GWT para el equipo. Después de leer durante algunos días -dado que soy un novato en la materia- preparé la presentación que adorna este post. La pongo a disposición de los interesados.

Saludos, y hasta otra!

Spring Community Day 2010


Creo que debo empezar esto post disculpándome por la ausencia prolongada: He andado tan ocupado últimamente que hasta se han reducido mis horas de sueño. Sin embargo, prometo encontrar espacio para actualizar el blog y evitar que se llene de telarañas. Palabra.

Luego, quería invitar a los lectores peruanos al Spring Community Day 2010 que organiza  la comunidad Spring Perú. Habrá ponencias técnicas, presentación de casos prácticos, espacios de discusión y el infaltable coffee break (además, este blogger tendrá a su cargo una charla sobre Spring Web Services xD).

El evento se realizará el 27 de Noviembre (este sábado) en el Campus de la UPC. Para ingresar es necesario colaborar con 10 soles que serán destinados íntegramente a financiar una Misión de Navidad para los niños del Asentamiento Humano San Alvino.

Están todos cordialmente  invitados.

Cohesión y el principio de responsabilidad única

Imaginemos que nuestro siempre creativo usuario nos solicita un proceso que genere cierto reporte Excel con información de base de datos y se lo coloquemos en un servidor FTP para que lo pueda descargar luego. Dado que -para variar- quiere el reporte para ayer, construimos la siguiente solución apresurada:

Diseño improvisado
Terminamos, el usuario lo prueba, mira que es bueno y nos felicita. 

Pasan unos días y el mismo usuario nos pide una aplicación para que pueda colocar algunos documentos PDF en el mismo  servidor FTP. ¿Por qué no utilizamos a nuestra clase AdministradorDeReportes junto a su método enviarArchivoFTP()? Podríamos hacerlo, pero nuestra clase para poder ser compilada depende también de POI y de JDBC lo que le daría una carga extra de 5 megas a nuestro sencilla aplicación para PDF's. Esto pone en evidencia que nuestro diseño original tiene algunos problemitas.

La cuestión es que nuestra clase AdministradorDeReportes tiene la responsabilidad de conectarse a base de datos, de generar un archivo Excel con esta data y de colocar el archivo en el servidor FTP; y si uno quiere reutilizar este componente pues no le queda otra que adquirir el paquete completo de responsabilidades que esta clase posee. ¿Y si queremos otra clase que genere el mismo reporte en HTML en un browser? Sería bastante conveniente utilizar el método obtenerInformacionBD ()de AdministradorDeReportes, pero una vez más tenemos el problema que esta clase tiene dependencias que no necesitamos (ni el API para conexión a FTP ni la generación de Excel). Dado que es obvio que nuestro diseño inicial tiene problemas, corregimos y planteamos lo siguiente:

Después de recapacitar, planteamos esto
Con esta nueva solución, la aplicación para transferir archivos PDF solamente debería utilizar la clase ClienteFTP sin la sobrecarga del resto de API's que no necesita; y si se requiere el reporte HTML que planteábamos sólo dependeríamos de la clase ExtractorDeDatos. Este diseño tiene la peculiaridad que nuestras clases están bastante especializadas y tienen una única responsabilidad específica: Por ejemplo, la clase ClienteFTP se ocupa de la conexión con el servidor FTP y nada más. Es esta especialización lo que nos da bastante flexibilidad para la reutilización, que es lo que trata de decirnos Robert C. Martin con su Principio de Responsabilidad Única:

“Nunca debe de haber más de una razón para que una clase sea modificada”

Si vemos nuestro diseño inicial, la clase AdministradorDeReportes debería modificarse si es que se requieren datos extra en el reporte. Esta clase también debería modificarse si quieren que el reporte Excel tenga otros colores. Y si quisiéramos optimizar la conexión con el servidor FTP nuestra clase AdministradorDeReportes también debería pasar por mantenimiento, por lo que tenemos un ejemplo bastante claro de como violentar este principio. Ahora, en nuestra versión mejorada del diseño hemos transferido estas responsabilidades a las clases ExtractorDeDatos, ClienteFTP y ReporteadorExcel; de modo que cada cambio que mencionábamos  ahora impacta solamente en una clase de nuestro programa.

Casos de uso y descomposición funcional

Imaginemos que estamos capturando requerimientos para una página web que tiene que incluir un foro. Hablamos con el usuario líder, y nos cuenta que es necesario que  el administrador del sistema esté en capacidad de bloquear el acceso a los usuarios que repetidamente muestren un comportamiento inadecuado en el foro (escriban en mayúsculas, resuciten threads antiguos, utilicen palabras malsonantes y otras exquisiteces de los foristas). Después de la entrevista vamos a nuestra oficina, iniciamos nuestra herramienta CASE y aplicamos la técnica de casos de uso según lo que nos enseñaron en la universidad para producir este diagrama:

Diagrama construido con StarUML

Esta primera versión tiene algunos inconvenientes. Por definición, un caso de uso debe producir valor para los actores con los que interactúa (según Kurt Bittner) y no creo que el Administrador encuentre mucho valor a la tarea de "Buscar Usuarios" o de "Mostrar Información de Usuario". A menos que se trate de reportes -y no es el caso- el Administrador del Foro no va a entrar a nuestra aplicación sólamente a realizar una búsqueda o a consultar información de un usuario, sino lo que desea es estar en capacidad de bloquear a los usuarios rebeldes. Por ahí ya tenemos problemas.

Ahora, tal vez nuestra intención inicial de agregar tantos casos era para poner en evidencia que para bloquear a un usuario es necesario primero buscarlo, luego mostrar un formulario con su información básica, después  ingresar los motivos por los que se le va a bloquear para finalmente realizar el bloqueo. Si ese fue el caso caimos en otro error, dado que los casos de uso no "invocan" a otros casos de uso y mucho menos se comunican entre ellos. Realizar esto es intentar convertir los casos de uso en funciones, lo que nos lleva al problema de la descomposición funcional: Descomponer un problema en partes pequeñas y aisladas entre sí, que trabajando juntas nos proveen de una funcionalidad del sistema.

La descomposición funcional de casos de uso hace que estos pierdan contexto, como el caso de uso "Asignar motivos de bloqueo" que no tiene sentido sin el resto de casos de uso del diagrama. También está el tema del valor para los actores que mencionábamos al inicio, el  mismo caso de uso "Asignar motivos de bloqueo" no le va a servir al Administrador si es que el usuario al final no termina bloqueado.

Ahora ¿qué hacemos para no caer en esto?. Navegando me topé con un documento de Kurt Bittner en el que nos sugiere tres cosas:

  1. Centrarse en casos de uso que provean valor real a los stakeholders. En el caso de nuestra administrador, buscando usuarios no gana mucho.
  2. Limitar el uso de "includes" sólamente para representar descripciones comunes entre caso de uso. Y usarlas con muchísimo cuidado.
  3. Limitar el uso de "extends" sólamente para agregar comportamientos opcionales a casos de uso existentes que por si mismos generen valor. Personalmente, no me gusta mucho utilizar esta relación.

Entonces, sabiendo esto borramos nuestro primer borrador defectuoso y lo cambiamos por este mucho más simple y elegante:

Este diagrama también lo hice con StarUML


En la especificación de caso de uso ya podemos explayarnos describiendo el flujo deseado.  Recuerden que un modelo de casos de uso es principal y mayoritariamente texto. Eso sería todo en esta entrega, hasta otra!

No hables con extraños, o la Ley de Deméter

Imagen: Autorretrato con Sombrero de Paul Gauguin

Desde los primeros cursos de orientación a objetos de la universidad he escuchado que al programar se debe procurar siempre "niveles bajos de acoplamiento y altos de cohesión". Recién ahora que estoy ojeando The Pragmatic Programmer de  Andrew Hunt y David Thomas el tema del acoplamiento me ha quedado claro y quería compartir con ustedes mis hallazgos.

Es más fácil entender este concepto si vemos un programa que no lo aplica. El libro propone este ejemplo:

          
     public void plotDate(Date aDate, Selection aSelection) { 
       TimeZone tz = aSelection.getRecorder().getLocation().getTimeZone(); 
       ... 
     } 

La clase que contiene al método plotDate está altamente acoplada, dado que su estructura actual depende de otras tres clases: Selection, Recorder y Location. Esto nos pone en una situación delicada, dado que un cambio en cualquiera de esas tres clases tendría impacto en nuestro programa. Por ejemplo, si Location hace que el método getTimeZone sea privado, nuestro método tendría que redefinirse, y lo mismo pasaría con modificaciones a cualquiera de las otras clases dependientes. Es por eso que se recomienda mantener los niveles de acoplamiento mínimos (es decir, depender del mínimo número de clases) para evitar que las ocurrencias del resto deshagan nuestro trabajo. Si bajamos el nivel de acoplamiento de clase de arriba, nos quedaría algo así:

      
     public void plotDate(Date aDate, TimeZone aTz) { 
       ... 
     } 

Ahora nuestro método espera que le llegue una instacia de TimeZone como parámetro y ya no la obtiene desde un objeto Selection. De este modo hemos reducido las dependencias de tres a una clase (sólamente TimeZone ), siendo responsabilidad de la clase cliente la obtención de la instancia de TimeZone que necesitamos. Esta clase cliente nos puede invocar de esta manera:

     plotDate(someDate, someSelection.getTimeZone()); 

El libro menciona que los síntomas clásicos de un programa altamente acoplado son que los cambios más simples impactan en componentes que no deberían, y que los programadores se resisten a darle mantenimiento al sistema por miedo a echar a perder algo.

Descrito al problema, ahora toca describir la solución.

La ley de Deméter -inventada en 1987 en la  Northeastern University - tiene como objetivo el minimizar las dependencias entre las clases de un programa dado, previniendo el acceso a objetos externos para consumir sus métodos. En resumen, la ley establece lo siguiente:

Cualquier método de un objeto debe invocar solamente a los siguientes métodos:

  • Sus propios métodos
  • Los métodos de los objetos que ha recibido como parámetros
  • Los métodos de los objetos que posee como atributos
  • Los métodos de los objetos declarados como variables locales 


Con esto aseguramos el mínimo de dependencias posibles. Los dejo con una imagen extraída del libro que ilustra el concepto:

Imagen tomada de The Pragmatic Programmer de Andrew Hunt y David Thomas

Hasta otra!

Código vicioso



(Imagen: El Fumador de Vincent Van Gogh)


El que una aplicación funcione no significa siempre que esté bien construida, y es frecuente que detrás de una bonita y funcional interfaz de usuario se encuentre un pandemónium de líneas de código. Los americanos les llaman a estas joyitas de la programación "code smell"  que vendrían a ser "Cualquier síntoma en el código fuente de un programa que nos indique la presencia probable de un problema mayor" según Wikipedia.

También en Wikipedia encontramos una pequeña lista de defectos famosos. Les listo algunos que me parecieron interesantes y frecuentes:

  • Código duplicado: Es el producto de una de las más antiguas y populares técnicas de programación: el "copy-paste". A la larga representa un problema serio ya que los cambios que se realicen en una sección de código deben ser replicados en el resto de archivos donde hemos "reutilizado" esta funcionalidad. Para evitarlo podemos extender la clase, o simplemente encapsular esta funcionalidad en un método que sea invocado por los que lo requieren.

  • Clases extensas: Las convenciones de código en Java recomiendan que las clases no debe exceder las 2000 líneas. Si ese es el caso, probablemente tu clase esté haciendo más de lo que debería violentando el principio de alta cohesión. Las clases deben tener responsabilidades especializadas, y si nuestro programa cuenta con Objetos-Dios que hacen de todo es que tenemos un serio problema de diseño.

  • Envidia funcional: Sucede cuando una clase utiliza en exceso los métodos de otra clase. Imaginémonos una clase ClaseEnvidiosa que tiene un método calcularPromedio(). En calcularPromedio(), invoca al método obtenerNumeroDeCursos() de la clase Estudiante. Luego, invoca a obtenerCalificacion(int i) de Estudiante  varias veces para sumar todos los valores obtenidos y al resultado dividirlo entre el número de cursos que obtuvo anteriormente. Finalmente, invoca a setPromedio(double d) de Estudiante y le asigna el valor calculado. Se observa que ClaseEnvidiosa invoca en exceso a los métodos de Estudiante desde el método calcularPromedio(), cuando lo más práctico es que el método calcularPromedio() se encuentre en la misma clase Estudiante.

  • Complejidad artificial: Utilizar en exceso patrones de diseño cuando es suficiente con un diseño sencillo. Recuerdo una aplicación donde el controlador Struts invocaba  a un Facade, que a su vez invocaba a un BusinessDelegate que mediante un EJB hacía uso de una clase DAO que finalmente invocaba a un procedimiento almacenado. Y lo único que se hacía en cada capa de la aplicación  era pasar los parámetros a la capa inferior, hasta llegar al DAO que era el que realmente hacía el trabajo. ¿No era más sencillo contar con un Facade y que éste invoque al DAO directamente?

  • Clase ociosa: Cuando una clase hace  muy poco. Por ejemplo, una clase con un sólo método que es invocado solamente por una clase. Tampoco hay que llevar lo de alta cohesión a extremos y para estas situaciones recomendaría que la funcionalidad de la clase ociosa sea absorbida por otra clase más robusta.

 Wikipedia señala más, pero esas son las que he encontrado (o he incurrido xD). Hasta otra!