Consejos para crear y mantener un perfil de LinkedIn

Varias veces me han pedido consejos sobre como construir un buen perfil en LinkedIn. La última vez, fue un ex-estudiante y amigo quien me pidió ayuda. Tuvimos una conversación por chat al respecto, y al terminar, le pedí que me enviara la conversación en limpio para publicarla.

Foto

Si por favor. De nuevo, si por favor, ponga una foto. Le sugiero una foto donde se vea bien, profesional. Una foto de cerca donde se le vea el rostro. En general la foto es importante, por ejemplo en lo personal tengo por regla general no aceptar a ningún contacto si no tiene foto (a menos que sea una persona que conozca de primera mano).

Idioma

Lo ideal sería tenerlo en Español/Inglés, LinkedIn lo soporta. Ahora bien, sé por experiencia que mantener un perfil en dos idiomas es un verdadero dolor de cabeza, yo lo hice durante mucho tiempo y me trajo más trabajo (en el mal sentido de la palabra, es decir, esfuerzo desperdiciado) que ventajas. Así que, eventualmente llegué a un punto en el que decidí tener CV y LinkedIn sólo en Inglés. Esto probablemente dependa mucho del área en la que se trabaje, pero en la mía (desarrollo de software) funciona bastante bien.
La lógica detrás de esto es que si se desea trabajar fuera de Hispanoamérica (USA, Europa, Asia, etc), las empresas pedirán CV en Inglés, y ademas, para la mayoría de los trabajos buenos en Hispanoamérica o bien pedirán que se hable inglés y un CV en inglés, o bien la gente que toma la decisión es gente que habla Inglés, aún cuando el trabajo en sí no requiera inglés. A mi nunca me cerró una buena puerta/oportunidad tener mi perfil en Inglés o enviar un CV en Inglés.

Resumen (Summary)

Es buena idea escribir una breve frase describiendo en que punto de la carrera se está, que intereses y objetivos se tienen, que tipos de trabajos interesan, etc, es como un breve resumen profesional de si mismo. El resumen es útil para atrapar al lector, a veces si abro un perfil y sólo veo historia laboral no sigo leyendo, pero si lo abro y leo una breve buena descripción, bien pensada y bien escrita, es posible que me interese y siga leyendo lo demás (que es lo que se desea). Hay un aspecto de “mercadeo y publicidad” en todo este asunto, me parece que suele ser buena idea salir de lo estándar, quizá ser un poco disonante como para atraer sin perder la elegancia.

Advice for Contacting…

LinkedIn tiene esta sección que en español se traduciría “consejo para contactar a…”. En mi opinión es buena idea poner información o alguna sugerencia para gente que desee contactarnos, es útil poner el email ofuscado, por ejemplo:  usuario “at” gmail “dot” com. Eso ayuda a reclutadores y gente que no te conoce en LinkedIn a conectar contigo y a escribirte, sobre todo si no tienen LinkedIn premium.

Historia Laboral

No se limite a una simple descripción de los roles, las empresas y las fechas. Describa logros, retos que se enfrentaron, resultados. Es bueno poner capturas de pantalla, enlaces, presentaciones, videos, etc. de los trabajos realizados (sin violar ningún tipo de acuerdo de confidencialidad o copyright).

Proyectos

Si, creo hoy en día que hasta puede ser más importante poner proyectos que la misma historia laboral. Si son cosas que se prestan para poner screenshots/pdfs mucho mejor, igual en el trabajo, si se tienen buenos screenshots/videos LinkedIn permite ponerlos/vincularlos (ver Historia Laboral).

Skills (Habilidades)

No se deben escribir habilidades muy largas. Se debe ser cuidadoso en las habilidades que se seleccionan, deben ser cosas concretas, LinkedIn tiene un máximo de 50, eso parece mucho al comenzar, pero a la larga se quedan cortas, y cuando la gente comienza a endorsarlas (validarlas), duele tener que borrar algunas menos relevantes (como UML en mi caso) para poner cosas importantes como arquitectura de software, testing, sistemas distribuidos, etc. Esto en particular me ha pasado, la gente me endorsa por skills que en estos momentos me interesan poco: Java, UML, MySQL y no tengo espacios para habilidades hacia las que mi carrera se ha movido.
Se deben seleccionar habilidades que realmente se tengan. Se debe evitar poner cosas al principio solo por poner/llenar, es preferible tener poco sólido que mucho con muy poca experiencia.
También es importante alinear las habilidades con lo que se quiere profesionalmente, por ejemplo, si se quiere un trabajo de developer, poner “Microsoft Word” probablemente sobra.

Premios y Reconocimientos

Si se tienen, no se deben dejar de mencionar, esto no es ser presuntuoso, es de hecho algo común para un developer Junior recién salido de la universidad. A medida que se va ganando experiencia se va notando que están de más, y que es más importante la experiencia y otras cosas. En ese momento saldrán de forma natural del CV o del perfil (por ejemplo porque se pasa de dos paginas y se desea mantenerlo en sólo una). Pero eso es una decisión a largo plazo.
Si se ha recibido un mismo reconocimiento repetidas veces, no recomiendo mencionarlo por cada una de éstas. Es preferible poner algo como: “Ganador de la orden XYZ tres veces consecutivas desde el año A hasta el año B” o “Ganador de la orden XYZ tres veces los años A, B y C”.

Cómo Crecer la Red de Contactos

Con paciencia: es algo que toma tiempo. Primero con los amigos y/o gente conocida que sea relevante profesionalmente, luego con personas con las que se tengan intereses comunes, reclutadores, etc. Luego con personas que se van conociendo poco a poco a lo largo de la vida profesional.
En lo personal no recomiendo aceptar/invitar gente que tenga perfiles de baja calidad, es decir, aquellos que no posean foto ni información relevante, o gente que tenga perfiles que no tienen nada que ver con las áreas de interés (hay excepciones).
Es bueno conversar con la gente, cuando alguien me invita a conectar, si su perfil me resulta interesante generalmente escribo un mensaje de cortesía (“gusto en conocerlo por este medio, no dude en contactarme si desea conversar sobre algún tópico en particular, etc”). Si un reclutador me contacta por alguna oportunidad que en el momento no me interesa, suelo responder también con algún mensaje de cortesía que deja las puertas abiertas, a lo que usualmente le sigue una invitación a conectar.
Normalmente cuando soy yo el que envía una invitación a conectar, me tomo la molestia de escribir algo en lugar de enviar sólo el mensaje por defecto de LinkedIn. Eso usualmente muestra interés real en contactar e incrementa las probabilidades de que la otra persona acepte la invitación.

Generalidades

Es bueno seguir grupos/foros sobre tópico de interés profesional. Participar en los grupos mucho mejor: escribir preguntas, escribir respuestas, etc. Toma tiempo pero da visibilidad (yo lo hice en una época pero desde hace rato que no tengo tiempo para hacerlo).
También es aconsejable seguir empresas interesantes. De esa forma es posible conocer y contactar mucha gente.
Por último, hay que recordar que es una red profesional, no una red social, los posts/comentarios se deben mantener profesionales. Las fotos de gatitos y de familia no son buena idea. Una vez elimine a alguien porque publicaba mensajes religiosos cada dos días (no tengo nada en contra las religiones de los demás, las respeto a todas por igual, pero no es un tema de interés en LinkedIn).
En fin, ¡Happy LinkedIn y hasta la próxima!

Hello World!

Este es mi “Hola Mundo” recargado.

Este sitio es derivado de CodeCompiling, que fue el sitio web que mantuve mientras fui profesor en la Universidad de Los Andes en Venezuela.

La idea es mantener el sitio original (CodeCompiling) como página/blog sobre tópicos de ingeniería de software, mientras que éste sitio va a ser mi blog personal. Es posible que también hable aquí un poco sobre ingeniería de software, pero más a modo de especulación que de otra cosa, para quejarme de las ITs, contar algunas pesadillas, etc, es decir, en CodeCompiling voy a mantener los tópicos serios.

Me pareció buena idea copiar el post inicial de CodeCompiling, que va a continuación:


System.out.println(“¡Hola Mundo!);

¿Qué mejor forma de estrenar un blog sobre desarrollo de software que exclamar “hola mundo”?

En realidad me estoy quedando un poco corto sobre las intenciones de este blog, decir que es únicamente un sobre desarrollo de software es ser un poco impreciso. Pienso publicar cualquier disparate interesante que se me cruce por la cabeza. El contenido va a ir desde enlaces y recomendaciones sobre música y películas, libros que me agradan, fotografías, etc. Por cierto, disparates se me ocurren muchos, de ahí a que sean interesantes… eso es otra cosa. Eso si, de seguro muchos de esos disparates van a estar relacionados con la ingeniería de software, las bases de datos, el desarrollo y programación de juegos y la programación en general, que son áreas que me interesan mucho y que practico profesionalmente en algunos casos y a las que soy aficionado en otros.

Además, debo decir también que el nombre y el logo de CodeCompiling me parecen de lo más apropiados para el sitio, bueno al menos a mi me gustan y eso en el fondo es lo importante.

code_compiling_logo

De donde salió el nombre o el logo, no se, supongo que fue inspiración, un día simplemente estaban allí y no me los podía quitar de la cabeza, estuve dando vueltas unos cuantos días antes de comprar el dominio y el hosting hasta que un día me decidí y nació CodeCompiling.

Escribir un blog no es una tarea fácil, se de muchos que lo han intentado y han fracasado, yo mismo lo intenté en una ocasión y no llegué muy lejos. Sin embargo, a diferencia de aquella vez, creo que ahora tengo mucho que decir, y si logro encontrar el tiempo para escribir entonces es posible que salga algún que otro post interesante.

¡Saludos y nos vemos en CodeCompiling!

SOLID y Visitor: Un ejemplo explicado (2da Parte)

En un artículo anterior comenzamos una diseñar un componente que permitía recorrer directorios recursivamente y procesar los archivos que fuese encontrando en su recorrido. Todo esto se relacionó con los principios SOLID y el patrón de diseño visitante. En este artículo se terminará de diseñar el componente.

Cuarta Aproximación

Un siguiente paso sería desacoplar (sacar) las reglas que verifican si se puede entrar en un subdirectorio y si se puede procesar un archivo de FileScanner, una mala solución (ya veremos la razón) sería esta:

SOLID_visitor_4_th(click para agrandar)

Como se puede apreciar en el diagrama, se agregaron dos métodos a la interfaz Visitor. Ambos métodos retornan un booleano y responden a las preguntas de si un subdirectorio se debe recorrer (isTraversable) y si un archivo se debe procesar (isProcessable).

Si se analizan las responsabilidades de estas clases tenemos:

Responsabilidades de FileScanner:

  • Recorrer recursivamente los directorios / subdirectorios a partir del directorio raíz pasado en el método scan, invocando al visitante (Visitor) para delegarle la responsabilidad de decidir:
    1. Qué directorios / subdirectorios se deben recorrer.
    2. Qué archivos se deben procesar.
    3. Procesar los archivos que correspondan.

Responsabilidades de Visitor:

  • Servir de interfaz “abstracta/genérica” para que FileScanner pueda:
    1. Saber si un directorio se debe recorrer recursivamente o no.
    2. Saber si un archivo se debe procesar o no.
    3. Invocar al algoritmo de procesamiento de archivos.

Responsabilidades de DoSomethingVisitor:

  • Implementar Visitor y satisfacer sus tres responsabilidades definidas.

Si bien FileScanner tiene una sola responsabilidad, Visitor y DoSomethingVisitor parecieran tener demasiadas responsabilidades y más adelante hablaremos del impacto que esto tiene.

En cuanto a la reusabilidad, FileScanner es completamente reusable en otros contextos en los que se necesite recorrer un directorio y sus subdirectorios para procesar de cierta forma los archivos contenidos en éstos. Las reglas que filtran directorios y archivos y el algoritmo de procesamiento se implementan como realizaciones de la interfaz Visitor, de modo que, si a FileScanner se le brinda una implementación adecuada de Visitor puede reusarse en cualquier contexto.

La reusabilidad de las implementaciones de Visitor es más dudosa, debido principalmente a que mezcla tres responsabilidades que pareciera no tienen mucho que ver entre si, es decir, determinar si un subdirectorio debe recorrerse, si un archivo debe procesarse y realizar el procesamiento del archivo son cosas que no están necesariamente relacionadas entre si, y que sin embargo con esta estructura se están implementando en la misma clase. Cuando se tiene un componente con estas características, es decir, muchas responsabilidades no directamente relacionadas entre sí, se dice que el componente tiene baja cohesión.

Por ejemplo, se pueden tener las siguientes implementaciones de Visitor para distintos contextos:

  • MyVisitorA: (1a) Excluye los directorios ocultos (isTraversable), (1b) excluye cualquier archivo que NO TENGA extensión .java, es decir, procesa los .java (isProcessable) y (1c) elimina el archivo a procesar (visit).
  • MyVisitorB: (2a) Excluye los directorios “CVS” “.svn” y “.hg” (isTraversable), (2b) excluye cualquier archivo que SI TENGA extensión .java, es decir, procesa todo menos los .java (isProcessable) y (2c) saca una copia de respaldo del archivo a procesar (visit).

La funcionalidad de las clases anteriores es totalmente disyunta, es decir, no hay comportamientos comunes entre las dos clases. Ahora bien, que sucedería si se quiere una tercera implementación que haga lo siguiente:

  • MyVisitorC: (*1a*) Excluye los directorios ocultos (isTraversable), (*2b*) excluye cualquier archivo que SI TENGA extensión .java, es decir, procesa todo menos los .java (isProcessable) y (3c) renombra el archivo a procesar (visit).

Dado que esto puede resultar algo confuso, la siguiente figura muestra la relación existente entre las reglas implementadas por MyVisitorA, MyVisitorB y MyVisitorC.

SOLID_visitor_5

MyVisitorC usa la regla de filtrado de directorios (*1a*), es decir la implementada en MyVisitorA, la regla de selección de archivos a procesar (*2b*), es decir la implementada en MyVisitorB, y luego implementa una nueva regla de procesado de archivos (3c).

Ahora bien, tal y como está definido Visitor y sus implementaciones, es muy difícil implementar MyVisitorC y reutilizar las implementaciones ya existentes de (*1a*) y (*2b*) en MyVisitorA y MyVisitorB, porque al estar implementadas en las mismas clases que (1b) y (1c) por un lado y que (2a) y (2c) por el otro, no se pueden tomar por separado sin tener que arrastrar las implementaciones de las otras reglas que no se necesitan.

En el ejemplo del virus y el anti-virus, se puede hacer el siguiente análisis: tanto el visitante del virus como el del antivirus procesarán cualquier directorio (isTraversable), procesarán archivos .exe, .com y .dll (isProcessable), pero uno infectará con virus los archivos procesados y el otro removerá los virus (visit). Si imaginamos una empresa que se dedique a implementar anti-virus, pero que maliciosamente también implemente virus, entonces la empresa se encontrará con las siguientes clases:

SOLID_visitor_7

El problema es que la clase VirusVisitor y AntiVirusVisitor comparten la misma implementación de los métodos isTraversable e isProcessable (en azul), pero tienen implementaciones completamente distintas de los métodos visit (en rojo), donde una infecta un archivo y la otra elimina la infección. Con esta estructura no hay una manera sencilla y directa de compartir la implementación común entre isTraversable e isProcessable más que el copy / paste o la delegacion de estos métodos a otras clases y su referenciación (lo que igualmente se considera duplicación de código) desde los métodos de las implementaciones de Visitor.

Más adelante se volverá a este punto y se mostrará una forma de resolverlo.

En cuanto a la facilidad de pruebas, parece que no tenemos mayores problemas. FileScanner depende de interfaces y recibe sus dependencias en el constructor, de modo que podemos inyectarle un Mock Object de Visitor y probarla independientemente de cualquier implementación particular de esta interfaz.

Las implementaciones concretas de Visitor, por ejemplo DoSomethingVisitor, también se pueden probar de forma independiente de FileScanner, y en este caso, pareciera que el exceso de responsabilidades de la clase y la falta de cohesión entre éstas no es un inconveniente a la hora de implementar pruebas.

El problema de reusabilidad es más grave, siendo su causa el exceso de responsabilidades y la falta de cohesión entre éstas en la interfaz Visitor y sus implementaciones. Cuando un grupo de responsabilidades que no tienen relación entre sí se atan a una sola interfaz y/o se implementan en una sola clase, el resultado es que no es posible reutilizar una sola de las responsabilidades en otro contexto, al menos no sin tener que arrastrar las otras responsabilidades que no se desean reutilizar.

En realidad, para “reutilizar” (si, entre comillas) esas responsabilidades, tendríamos que hacer un desvergonzado copy / paste (¿ahora entienden las comillas?) o hacer cualquier otra maroma, como enviar cada responsabilidad a una tercera clase e invocarlas o referenciarlas de las clases donde se van a reutilizar. El resultado sería código duplicado o excesivamente complejo, cosas nada deseables y que son “smells” que indican que algo anda muy mal en el código.

Quinta Aproximación

Una solución elegante es apegarse al ISP (Interface Segregation Principle) y dividir la interfaz Visitor en tres interfaces, una para cada responsabilidad actualmente existente en Visitor. Haciendo esto, el resultado es:

SOLID_visitor_6_th(click para agrandar)

Luego de hacer el último cambio, tenemos a FileScanner que depende de Visitor y de dos interfaces adicionales: Traversable y Processable. Visitor sigue cumpliendo la función que tenía la primera vez que la definimos, mientras que Traversable decide ahora si un subdirectorio debe recorrerse recursivamente o no, y Processable decide si un archivo debe procesarse o no. De esta forma, cada una de las tres responsabilidades asociadas anteriormente a la interfaz Visitor ha terminado en su propia interfaz, y la implementación anterior de DoSomethingVisitor que antes satisfacía estas tres responsabilidades ha quedado dividida en tres clases separadas: DoSomethingVisitor, que cumple la única responsabilidad que quedó asociada a Visitor, MyTraversable, que implementa la lógica particular a un contexto para decidir si se debe o no recorrer un directorio / subdirectorio y MyProcessable, que implementa la lógica para decidir si se debe procesar o no un archivo.

Las responsabilidades quedan de esta forma:

Responsabilidades de FileScanner:

  • Recorrer recursivamente los directorios / subdirectorios a partir del directorio raíz pasado en el método scan, invocando a la interfaz Traversable para (a) delegarle la responsabilidad de decidir que directorios / subdirectorios se deben recorrer, a la interfaz Processable para (b) delegarle la responsabilidad de decidir que archivos se deben procesar y a la interfaz Visitor para (c) procesar los archivos que correspondan. También es posible que FileScanner provea un comportamiento por defecto en caso de que Traversable y/o Processable sean nulos, este comportamiento podría ser recorrer y procesar todo.

Es importante mencionar que aunque FileScanner pareciera tener demasiadas responsabilidades, en el fondo todo el trabajo lo hacen Traversable, Processable y Visitor.

Responsabilidades de Traversable:

  • Servir de interfaz “abstracta/genérica” para que FileScanner pueda saber si un directorio se debe recorrer recursivamente o no.

Responsabilidades de Processable:

  • Servir de interfaz “abstracta/genérica” para que FileScanner pueda saber si un archivo se debe procesar o no.

Responsabilidades de Visitor:

  • Servir de interfaz “abstracta/genérica” para que FileScanner pueda invocar al algoritmo de procesamiento de turno.

Responsabilidades de MyTraversable:

  • Implementar Traversable y satisfacer la única responsabilidades definidas para Traversable.

Responsabilidades de MyProcessable:

  • Implementar Processable y satisfacer la única responsabilidades definidas para Processable.

Responsabilidades de DoSomethingVisitor:

  • Implementar Visitor y satisfacer la única responsabilidades definidas para Visitor.

Como se puede ver, cada clase / interfaz tiene asociada una única responsabilidad, lo que implica desde el punto de vista de la reutilización que las distintas implementaciones de las interfaces se podrían utilizar de forma independiente de las demás implementaciónes.

En cuanto a la facilidad de prueba no se han hecho cambios que hagan más difíciles las pruebas. FileScanner sigue dependiendo de interfaces y las dependencias se le inyectan en el constructor y por medio de setters, de modo que es posible crear un FileScanner con Mock Objects y probarlo de forma separada de cualquier implementación de Traversable, Processable y Visitor. Por otro lado, cualquier implementación de estas tres últimas interfaces se puede probar de forma independiente una de la otra, y de forma independiente de FileScanner.

¿Y luego qué?

Para terminar, les dejo un reto. En este punto, nuestra implementación está fuertemente acoplada a la clase File de Java. Esto no es en todos los casos malo, suponiendo que nuestro FileScanner debe recorrer únicamente directorios y archivos, pero sería bueno analizar el impacto en cuanto a reusabilidad y facilidad de pruebas. ¿Cómo se puede desacoplar File la clase FileScanner y de todas las demás clases e interfaces asociadas? ¿Cómo se rompe la dependencia entre FileScanner y File? ¿Qué pasaría si se quiere utilizar FileScanner para recorrer cualquier estructura arbórea? Evidentemente ya dejaría de ser un “FileScanner” y habría que evaluar si realmente es práctico llegar a esos niveles de generalización, pero en cualquier caso es un buen ejercicio mental. ¿Ideas? ¡Espero que si!

SOLID y Visitor: Un ejemplo explicado (1era Parte)

No es fácil encontrar ejemplos de los principios SOLID que sean lo suficientemente “realistas” y “didácticos” al mismo tiempo.

El siguiente ejemplo surge de un fragmento de código de un trabajo de tesis que actualmente estoy dirigiendo, y sirve para hablar de SRP (Single Responsibility Principle), OCP (Open-Closed Principle), ISP (Interface Segregation Principle) y DIP (Dependency Inversion Principle). El ejemplo resulta interesante porque abarca casi todos los principios SOLID y el único que queda por fuera es el LSP (Liskov Substitution Principle). Adicionalmente se muestra el uso del patrón Visitor, que es un patrón de diseño útil en algunos casos.

Problema

El problema a resolver es el siguiente: Dado un directorio (un File en Java), se debe recorrer el directorio y todos sus subdirectorios, y para cada archivo encontrado, es necesario realizar cierto proceso. En algunos casos, dependiendo de ciertas reglas, algunos subdirectorios se deben ignorar, por lo que no es necesario recorrerlos recursivamente. También existen casos en los que según ciertas reglas algunos archivos se deben ignorar y no deben ser procesados.

Este artículo tiene su origen en el código de una herramienta de internacionalización (I18N) para Java, de modo que lo que se recorre es un árbol de directorios que contiene código fuente. Entre los directorios que se ignoran están los “CVS”, “.svn” y “.hg”, entre otros, y los archivos que se deben procesar son todos aquellos con extensión “.properties” que comiencen con cualquiera de los prefijos “I18N”, “Numb” y “Date”.

Para no entrar en los detalles técnicos de la herramienta de internacionalización, en algunos casos a lo largo de este artículo se usarán como ejemplo dos posibles aplicaciones de esta idea: La primera es un anti-virus y su misión es recorrer un conjunto de directorios y procesar los archivos “.com”, “.exe” y “.dll”, buscando posibles virus y eliminándolos si los encuentra. La segunda es todo lo contrario, es decir es un virus, su misión es recorrer un conjunto de directorios, también buscando archivos “.com”, “.exe” y “.dll” para infectarlos.

En algunos casos se hablará de la funcionalidad general, es decir, de un componente que debe recorrer un conjunto de directorios filtrando según ciertas reglas cuáles recorre recursivamente y cuales ignora, y procesar o no también según ciertas reglas algunos archivos. En otros casos nos referiremos al ejemplo del antivirus – virus para poder resaltar algunos aspectos asociados a la reusabilidad.

Primera Aproximación

SOLID_visitor_1

La primera aproximación es un enfoque monolítico donde la única clase (FileScanner) tiene un único punto de entrada (scan) y hace absolutamente todo el trabajo.

Veamos que sucede si analizamos esta clase desde tres puntos de vista: Responsabilidades, Reusabilidad y Facilidad de Prueba.

Las responsabilidades de la clase son:

  • Recorrer recursivamente los directorios/subdirectorios a partir del directorio raíz pasado en el método scan.
  • Filtrar (decidir) cuales subdirectorios deben recorrerse recursivamente y cuales deben ignorarse.
  • Filtrar (decidir) cuales archivos deben procesarse y cuales deben ignorarse.
  • Procesar cada uno de los archivos encontrados.

En total podemos enumerar al menos cuatro responsabilidades diferentes en una sola clase (Baja Cohesión).

Sobre la reusabilidad del componente: Es reutilizable sólo en situaciones en las que las reglas de filtrado para directorios y archivos y el algoritmo de procesamiento de los archivos sean exactamente las mismos que en la implementación original, es decir, no es muy reutilizable (de hecho podríamos decir que no es nada reutilizable).

Sobre la facilidad de pruebas: Sólo podemos hacer pruebas de integración/sistema, es decir, pruebas fin a fin. No es posible hacer pruebas para los filtros individuales (de archivos o subdirectorios) o para el algoritmo de procesamiento de los archivos, porque todas las responsabilidades están encapsuladas en una misma clase y tenemos sólo un punto de entrada. Evidentemente esto es malo y hace que escribir pruebas para esta clase sea difícil.

En general aquí tenemos varios “smells” (olores de código/arquitectura, síntomas que nos dicen que hay algún problema de diseño). (1) La clase tiene demasiadas responsabilidades y hace muchas cosas, (2) la clase es larga (dado el punto anterior seguro que será larga), (3) la clase no es muy reutilizable en contextos distintos (no es flexible), y finalmente, (4) la clase, o más bien sus responsabilidades, son difíciles (o imposibles) de probar.

Para resolver el problema deberíamos primero revisar el SRP (Single Responsibility Principle / Principio de Responsabilidad Única) y tratar de romper la clase en varias partes de manera que podamos distribuir las responsabilidades.

En este caso particular, se puede utilizar el patrón visitante, o al menos una versión inicial del patrón visitante, porque en teoría no se estaremos usándolo sino hasta el siguiente refactor en el que incluyamos la interfaz Visitor. La idea general del patrón visitante es que dada una estructura de datos o un conjunto de objetos, se escribe un algoritmo que recorre dicha estructura y que para cada elemento en ella invoca a un método particular en una interfaz dada (el visitante), permitiendo procesar o hacer algo con el elemento actual. Lo interesante de este patrón, es que de hecho separa el recorrido de una estructura de datos del procesamiento de cada uno de los datos de la estructura de datos.

Segunda Aproximación

SOLID_visitor_2

En este escenario, FileScanner hace el recorrido de los directorios y el filtrado de los subdirectorios/archivos y cuando consigue un archivo para procesar invoca a visit en DoSomethingVisitor.

Hagamos el mismo análisis basado en responsabilidades, reusabilidad y facilidad de pruebas:

Responsabilidades de FileScanner:

  • Recorrer recursivamente los directorios/subdirectorios a partir del directorio raíz pasado en el método scan.
  • Filtrar (decidir) cuales subdirectorios deben recorrerse recursivamente y cuales deben ignorarse.
  • Filtrar (decidir) cuales archivos deben procesarse y cuales deben ignorarse.

Responsabilidades de DoSomethingVisitor:

  • Procesar cada uno de los archivos encontrados.

Es claro que este aspecto ha mejorado porque ahora las responsabilidades se han distribuido entre dos clases.

Sobre la reusabilidad: Sigue siendo poco reutilizable, porque sucede lo mismo que el escenario anterior. En general, la lógica del filtrado sigue codificada directamente en FileScanner y si bien la lógica del procesamiento está ahora codificada en DoSomethingVisitor, existe una dependencia directa entre las dos clases y aparentemente FileScanner crea su dependencia, es decir, crea una instancia de DoSomethingVisitor, lo que acopla fuertemente ambas clases entre si (o al menos acopla FileScanner a DoSomethingVisitor). Por esta razón no es posible cambiar la lógica de procesamiento según sea necesario en otro contexto, es decir, no es posible usar la implementación de FileScanner con otra lógica de procesamiento, al menos no sin cambiar el código de FileScanner.

Pensando en el ejemplo del antivirus – virus, no es posible compartir la misma implementación de FileScanner entre ambas aplicaciones porque simplemente esta clase está atada (acoplada) a DoSomethingVisitor, y para cambiar esto no queda más remedio que modificar el código de FileScanner. Vale decir en este punto que cortar y pegar no es una buena definición de reutlización, y que estar haciendo mucho copy/paste puede llegar a considerarse un “smell” de código también.

Sobre la facilidad de pruebas: Debido a que está implementado en una clase aparte, el algoritmo de procesamiento se puede probar de forma independiente de FileScanner, lo que en principio mejora un poco la situación con respecto al escenario anterior, pero FileScanner no se puede probar de forma independiente del algoritmo de procesamiento. Esto se debe a que existe una dependencia de una clase concreta y a que (probablemente) FileScanner crea esta dependencia, es decir, hace el new DoSomethingVisitor() en algún lugar de su código.

El siguiente paso podría consistir en tratar de mejorar la facilidad de prueba de FileScanner y en romper el acoplamiento/dependencia de FileScanner a un algoritmo de procesamiento concreto.

En relación con los principios SOLID, en este caso si se analiza FileScanner se puede ver que para modificar su comportamiento (lo que se hace con un archivo visitado) es necesario cambiar la implementación de DoSomethingVisitor, o peor aún, cambiar FileScanner para que apunte a una clase visitante que haga algo distinto. Este hecho, el tener que cambiar la clase (tocar el código) para cambiar su comportamiento, es una violación al OCP (Open-Close Principle / Principio Abierto-Cerrado). El OCP dice que las clases deberían estar abiertas para ser extendidas (no en el sentido de la herencia) pero cerradas para su modificación. Es decir, debería ser posible cambiar el comportamiento de un componente o clase sin que sea necesario cambiar el código de la clase. Esto puede sonar un poco confuso, pero en la próxima aproximación se verá un poco más claro.

Tercera Aproximación

SOLID_visitor_3

En este caso hemos introducido la interfaz Visitor y hemos hecho que FileScanner dependa de la interfaz en lugar de depender de un algoritmo o clase concreta. Además, y esto es extremadamente importante, estamos pasando la instancia usada de la interfaz en el constructor de FileScanner, de manera que ésta última clase ya no construye su dependencia concreta sino que se le “inyecta” en el constructor. Desde el punto de vista de FileScanner podría estar utilizando cualquier algoritmo de procesamiento, es decir, a FileScanner no le importa que algoritmo le pasen en el constructor, siempre y cuando se apegue al contrato definido por la interfaz Visitor.

Sobe SOLID, en este punto vale la pena mencionar el DIP o Dependency Inversion Principle / Principio de Inversión de Dependencias. Como hemos comentado, al FileScanner se le inyecta una instancia de Visitor en el constructor, pero ¿qué pasaría sin no le pasaramos la instancia en el constructor, sino que hicieramos algo como esto:

private Visitor visitor;

public FileScanner() {
//...
visitor = new DoSomethingVisitor();
//...

En efecto, estaríamos atando la funcionalidad del visitante a una interfaz (lo que es correcto) pero también estaríamos creando nuestra propia instancia (dependencia) concreta asociada a esa interfaz en el constructor. El punto es que de querer cambiar el comportamiento de FileScanner tendríamos que alterar el constructor, en especial cambiar el new para poder crear otra instancia concreta. El constructor anterior es una violación del principio DIP, y para resolver el problema se debería escribir de esta forma:

private Visitor visitor;

public FileScanner(Visitor visitor) {
//...
this.visitor = visitor;
//...

Es decir, la clase FileScanner no debería construir sus propias dependencias sino que estas deberían ser inyectadas en el constructor (o por setters) de forma que sea posible pasar diferentes implementaciones y generar comportamientos distintos (lo que es coherente con el OCP).

Hagamos el mismo análisis basado en responsabilidades, reusabilidad y facilidad de pruebas y veamos cómo ha valido la pena hacer este cambio:

Responsabilidades de FileScanner:

  • Recorrer recursivamente los directorios/subdirectorios a partir del directorio raíz pasado en el método scan, invocando al visitante pasado (Visitor) por cada archivo a procesar.
  • Filtrar (decidir) cuales subdirectorios deben recorrerse recursivamente y cuales deben ignorarse.
  • Filtrar (decidir) cuales archivos deben procesarse y cuales deben ignorarse.

Responsabilidades de Visitor:

  • Servir de interfaz “abstracta/genérica” para que FileScanner pueda invocar al algoritmo de procesamiento de turno.

Responsabilidades de DoSomethingVisitor:

  • Implementar Visitor y procesar cada uno de los archivos encontrados con un algoritmo particular.

En este aspecto no se ven muchos cambios, es decir, todas las clases tienen más o menos la misma cantidad de responsabilidades que tenían en el escenario anterior.

Desde el punto de vista de la reusabilidad: Hemos ganado mucho, porque ahora podemos usar FileScanner con cualquier algoritmo de procesamiento que sea necesario. Por ejemplo, podríamos tener una implementación de Visitor que saca una copia de los archivos procesados, otra que realiza cambios particulares y otra que borra los archivos, y todas las podríamos utilizar con el mismo FileScanner en diferentes contextos según sea necesario.

Pensando en el ejemplo del antivirus – virus, podríamos compartir/reusar la clase FileScanner (bueno, como veremos más adelante, no todavía, pero casi) y la interfaz Visitor tanto en el antivirus como en el virus, y lo único que cambiaría sería la implementación de la interfaz Visitor que le pasaríamos al FileScanner, es decir, en el caso del antivirus la implementación de Visitor removería los virus, en el caso del virus, la implementación infectaría los archivos procesados.

Lo importante, para cambiar el comportamiento es implementar un Visitor que haga lo que deseamos y parametrizar/configurar FileScanner con el Visitor correcto por medio del constructor antes de invocar el método scan.

Por otro lado, aún estamos atados al filtro de directorios/subdirectorios y archivos que está implementado en FileScanner. Este último punto es crítico, porque es lo único que aún no nos permite compartir el mismo FileScanner entre el antivirus y el virus, porque hay que recordar que el antivirus procesa los “.exe”, “.com” y “.dll”, mientras que el virus procesa sólo los “.exe” y los “.com”, es decir, los filtros entra ambas aplicaciones son distintos, pero como los filtros aún están codificados directamente en FileScanner, entonces eso genera un problema. Más adelante, al igual que como hicimos con el algoritmo de procesamiento, veremos que este no es un problema difícil de resolver.

Desde el punto de vista de la facilidad de pruebas también hemos ganado algo. El (los) algoritmos de procesamiento siguen siendo fáciles de probar de forma individual sin depender de FileScanner

Sin embargo ahora FileScanner es más fácil de probar, porque ahora no existe una dependencia directa entre esta clase y un algoritmo de procesamiento en particular, sino que ahora depende de una interfaz (Visitor) de la cual recibe una instancia en el constructor. De esta forma podemos probar FileScanner de manera independiente de cualquier algoritmo de procesamiento, esencialmente pasando en el constructor un Mock Object de Visitor, es decir, una implementación “falsa” de Visitor especialmente hecha para pruebas, que nos permite validar que sea invocada correctamente por FileScanner.

Más Adelante…

En una segunda parte de este artículo, seguiremos mejorando y generalizando la estructura de clases diseñada y continuaremos discutiendo los principios SOLID que aún faltan por mencionar.

ContextFreeArt: Arte Gráfico Generado por una GLC

Encontré un uso “artístico” de las Gramáticas de Libre Contexto. Ya hay algunos antecedentes en computación gráfica sobre aplicaciones orientadas a generar arte (que lamentablemente no tengo a mano), pero este es el primero en el que veo que se utiliza una gramática de libre contexto.

La aplicación se llama Context Free Art, el enlace es:

http://www.contextfreeart.org/

Es interesante mencionar que la búsqueda realizada en Google por medio de la que llegué a Context Free Art fue “context free grammar graphics computing”.

Context Free Art tiene su tiene su origen (o está inspirado) en una aplicación llamada SCIgen (An Automatic CS Paper Generator), que como su título indica, no es más que un “Generador Automático de Artículos (científicos) de Ciencias de la Computación”. Se puede encontrar más información sobre SCIgen en:

http://pdos.csail.mit.edu/scigen/

Esta última aplicación fue desarrollada por estudiantes de postgrado del MIT y no tengo claro aún, si lo hicieron sólo por diversión o con un objetivo científico/tecnológico concreto en mente. Aunque definitivamente suena a una buena broma. SCIgen también usa una gramática de libre contexto para generar de forma automática, imagino que dentro de ciertos parámetros y usando cierto lenguaje técnico codificado en la gramática, artículos científicos (con figuras, referencias y todo lo demás). Es evidente que, son artículos científicos totalmente inútiles, que no aportan nada. Sin embargo, es probable que alguien que no sepa mucho de computación (o un lector descuidado) caiga en la trampa y confunda un artículo generado por la aplicación por uno real. Por cierto que, el dolor de cabeza, el gran esfuerzo para tratar de entender un artículo generado por SCIgen y la frustración al no entender nada, probablemente son características que los artículos generados por esta aplicación comparten con muchos artículos científicos reales (¿humor académico?).

Inclusive, el grupo de SCIgen ha logrado que algunos de los artículos generados fuesen aceptados en conferencias (no es broma), y hasta han ido a las conferencias a defenderlos, según ellos, con una charla también completamente generada usando la misma técnica ¿curioso verdad? Los detalles de los artículos, e inclusive los detalles del arbitraje y la aceptación se pueden encontrar en el sitio web de SCIGen.

Volviendo a Context Free Art, la idea detrás de este generador de arte es definir una GLC que se utiliza para generar una figura (que usualmente está compuesta por otras figuras). Para lograr esto, al igual que en toda GLC hay una producción base y un conjunto de producciones alcanzadas desde la producción base, tal y como se muestra en el código del ejemplo 1:

S -> CIRCULO(55,55,5)RECTANGULO(50,50,10,10)ANIDAR(50,50,10/2,10/2)
ANIDAR(X,Y,W,H)     -> CIRCULO(X+W/2,Y+H/2,W/2)RECTANGULO(X,Y,W,H)
CIRCULO(X,Y,R)      -> terminal_circulo(X, Y, R)
RECTANGULO(X,Y,W,H) -> terminal_rectángulo(X,Y,W,H)
ANIDAR(X,Y,W,H)     -> vacío

(Código del Ejemplo 1)

El código anterior es una simplificación para poder explicar la idea, es decir, no está escrito aún usando la sintaxis de Context Free Art.

Excepto por los parámetros de las producciones, el código es muy similar a una GLC. La idea es que la gramática en lugar de generar un lenguaje formado por caracteres o símbolos, pueda generar un lenguaje formado por figuras. De esta forma, la gramática anterior genera el lenguaje de “todos los rectángulos que tienen un círculo inscrito dentro, y que a su vez, tienen recursivamente rectángulos y círculos de la mitad de tamaño del rectángulo padre”. Esto suena algo complicado por lo que es mucho más fácil visualizarlo con una imagen, de modo que se puede decir que el resultado del ejemplo 1 muestra dos “palabras” que estarán incluidas en el lenguaje descrito por la gramática anterior.

circulos_rectangulos
(Resultado del Ejemplo 1)

El ejemplo 2, fue uno de mis intentos de lograr una imagen atractiva utilizando la sintaxis de Context Free Art. Logré obtener el conjunto de cuadrados que se muestran en el resultado del ejemplo 2. Aún estoy intentando, sin mucho éxito pero acercándome poco a poco, escribir el código para generar el resultado de mi ejemplo inicial. El texto que está precedido por “#” son mis comentarios.

startshape begin_here  # Esta es la forma/producción inicial

CF::Background = [hue 120 sat 1 b -0.5]
CF::MinimumSize = 0.1  # Esto establece el máximo de recursión

shape begin_here { # La producción inicial
# Invoca a otra producción, equivalente a begin_here -> some_rect
 some_rect [x 100 y 100 size 100]
}

# Invocada por la producción anterior y por si misma
shape some_rect{
 SQUARE [x -1 y -1 size 0.5]
# Se invoca a si misma, equivalente a some_rect -> some_rect
 some_rect [x -1 y -1 size 0.5]
}

(Código del Ejemplo 2)

rectangulos
(Resultado del Ejemplo 2)

Un aspecto interesante es que en el código del ejemplo 2 pareciera que hay una producción recursiva que nunca termina, es decir, some_rect -> some_rect. No hay una producción nula, como es el caso de ANIDAR(X,Y,W,H) -> vacío en el ejemplo 1. Para solucionar este problema, Context Free Art expande las reglas recursivamente, hasta que las formas generadas tienen un mínimo de tamaño. Este tamaño, en el ejemplo 2 se define por medio de CF::MinimumSize = 0.1, o al menos es lo que entiendo hasta los momentos.

Mi ejemplo es el de un aprendiz que apenas tiene algunas horas entendiendo la sintaxis de Context Free Art, pero con un poco más de práctica y conocimiento, es posible usar los mismos principios para crear imágenes más complejas, tales como la que se muestra en el ejemplo 3:

complejo
(Resultado del Ejemplo 3)

Usando código tan simple como el siguiente:

startshape CIRCLES
CF::Background = [b -1]

shape CIRCLES
{
loop 12 [r 30] CONCENTRIC[x 100]
}

shape CONCENTRIC
{
loop 720 [r 0.5 h 0.5] SQUARE [x 100 b 1 sat 1]
CONCENTRIC[s 0.9 h 10]
}

(Código del Ejemplo 3)

Las Listas Azules

Cuento / Julio 2013

En el año 2113 el planeta estaba dividido en dos bandos. En cierto sentido se estaba librando una guerra, no una de esas que se pelean con tanques y balas, sino una nueva forma de guerra, una que se pelea en algo llamado Internet, específicamente en las Redes Sociales.

Dos posiciones políticas estaban enfrentadas, ambos bandos estaban muy parejos. Mientras que uno de ellos agrupaba al 51% de la población, el otro agrupaba al 49% restante. En realidad había un porcentaje de gente que no apoyaba a ninguno de los dos bandos, pero ese para muchos era un detalle insignificante. Ambos bandos de una u otra manera trataban de adjudicarse este porcentaje de gente.

Podría llamar “los verdes”, “los azules” o “los amarillos” a un bando y “los colorados” al otro, pero eso sería persistir en esa mezquina y miserable costumbre humana de asociar colores (lindas longitudes de onda) a cosas tan groseras y feas como movimientos políticos, religiones, etc, de modo que me voy a conformar con llamarlos 49%istas y 51%istas, o para ser breve, los 49 y los 51.

Se podría decir que los 49%istas estaban en desventaja numérica, y de hecho, los 51%istas afirmaban que eran una clarísima y contundente mayoría, por lo que los otros debían someterse absolutamente a su voluntad, pero en el fondo esto no era así, y los 49%istas representaban también una fuerza importante en el mundo. También los 49 decían que eran mayoría, y que los otros habían manipulado las estadísticas, aunque me parece que en el fondo los números eran bastante dinámicos y que en ocasiones los 49 tenían el 51% y los 51 pasaban a ser el 49%.

Como ya dije, si bien el asunto tuvo repercusiones en la realidad y en la vida cotidiana, dado que los 51%istas querían imponer su 51%ismo al otro bando, y probablemente también al contrario, uno de los campos principales de combate fue una red social llamada Critter, cuyo logotipo era una extraña especie de pájaro extraterrestre de color azul con dientes muy afilados (no pregunten).

En Critter se podían escribir mensajes cortos, mencionar a otros usuarios, etc, de modo que al principio todo se resumía a un simple toma y dame de insultos. Los 51%istas decían algo, los 49%istas respondían y se burlaban, los 51%istas se indignaban, los 49%istas argumentaban, los otros contra-argumentaban, y así el ciclo se repetía. Argumentos iban y venían, algunos muy razonables, otros en extremo absurdos. En algún momento se llegaba, de parte y parte a los insultos y las palabras fuertes, aunque debo decir que los 51%, por ser mayoría y controlar los poderes públicos en muchos casos abusaban.

Yo apenas tenía unos meses de estar registrado en Critter, cuando alguien “recritteo” un mensaje de un usuario que se llamaba #La_Molotov. El mensaje decía algo como:

“Este usuario #ErCommie, es una basura 51%ista, darle BAS masivo”

Debo decir que en algún momento de mi vida fui 51%ista, pero luego me harté, se pusieron muy pesados, gobernaban mal, fallaron en todo lo que habían prometido, y el mundo era un lugar… bueno, un tanto sucio. Los 49%istas no me simpatizaban mucho tampoco, en ese momento estaban disfrazados de corderos, pero uno veía a los personajes y a los voceros y en el fondo sabía que eran lobos. En cualquier caso, en ese momento al menos, los 49%istas eran una alternativa mejor a los 51%istas.

Me dio curiosidad y me puse a revisar la cuenta de #ErCommie. Realmente asqueroso, insultos van, toma, dale, y otro. Me harté. Aún quedaba por averiguar que era BAS. Finalmente descubrí que BAS viene de “Block and Spam”, que significa bloquear y declarar como “spam” (embasurador, bombardeador de publicidad, etc).

Resulta que “Critter” tiene una funcionalidad en la que uno puede marcar a un usuario como Spammer y al mismo tiempo bloquearlo. La idea es no ver más nunca mensajes de ese usuario, pero también, esta función le permite a “Critter” saber quienes son los usuarios problemáticos. Si mucha gente declara a un usuario como BAS entonces “Critter” bloqueará definitivamente (RIP) la cuenta de dicho usuario.

Claro que es una funcionalidad interesante, considerando todo el Spam que hay en el mundo… pero cuando está en las manos equivocadas y se usa de la forma incorrecta, eso es otra cosa.

Con esta nueva arma la guerra había escalado. Pasó de ser un asunto de simples argumentos e insultos a ser uno de BAS. Cuando te encontrabas algún extremista en la red simplemente BAS. Si alguien te insultaba… BAS. Si alguien decía algún argumento estúpido BAS… si alguien blandía un argumento interesante que la otra parte no podía refutar… BAS. Si, esto último no está bien, pero muchas veces ocurría.

Se formaron divisiones, como en el ejercito, #La_Molotov lideraba una de ellas, por eso el mensaje de BAS. Nuevamente revisé la cuenta de #ErCommie, pura basura, no me gustó. ¡Pum! BAS y así fue como disparé mi primer tiro en la guerra virtual.

Días después volví a revisar la cuenta de #ErCommie, muerta, difunta, RIP. La cuenta estaba indefinidamente bloqueada. De esa forma comencé a participar en la “guerra de las listas azules”, meter a alguien la lista azul significaba no sólo darle BAS, sino recrittear para que tus seguidores 49%istas le dieran BAS, mientras más tiros más posibilidades de que “Critter” bloqueara la cuenta atacada. Y claro… los 51%istas no se quedaban atrás, de hecho nadie estaba seguro de quién había disparado el primer tiro.

Las listas azules crecieron, los BAS proliferaron, las cuentas murieron, poco a poco. Yo disparé muchos tiros, #La_Molotov eventualmente cayó, RIP con su cuenta, alguien tomó su lugar como líder de la división, ese también cayó, luego tomó el liderazgo alguien llamado #El_Justiciero_BAS y luego otro, y otro, cada uno probablemente con un nombre más ridículo y absurdo que el anterior. Finalmente la guerra había terminado. Los 49%istas habían ganado pese a su aparente inferioridad numérica. Fue entonces cuando sucedió la división, y entre los mismos 49%istas comenzó el pleito, nadie sabe cual fue la diferencia de opinión, pero se comenzaron a hacer BAS entre ellos. Yo por su puesto tomé partido. BAS, BAS, BAS, BAS… y así sucesivamente, con el tiempo las cosas se fueron suavizando, y allí fue cuando no me gustaron las opiniones de #El_Heroico_Patriota. Demasiado insustanciales, ofensivas ¡Cómo no me di cuenta antes! BAS, BAS, BAS. RIP #El_Heroico_Patriota, el mundo era un lugar más tranquilo… y solitario.

En ese momento, con horror, me di cuenta de lo estúpido que había sido al participar en la guerra. Me di BAS a mi mismo y con eso fue bloqueado por spam el último usuario de “Critter”.


Dedicado a Julio Coll, quien en las “Las Columnas de Cyborg” escribió un cuento genial sobre las Listas Amarillas

¡Estaba en llamas cuando me acosté!

Cuento / Octubre 2010

¡Camina, camina, camina! El hombre se movía lentamente por los oscuros corredores. Al final del pasillo se veía un poco de luz. Con suerte, lograría alcanzar lo que aparentemente era al final del día una salida. Sigue caminando, arrastrando una pesada carga. Un pico en la mano derecha que utiliza de bastón y hace un rato usaba para extraer carbón.

catacumbas1

No quieren que se vaya. A cada paso que da se encuentra con algún desagradable demonio. Le hablan, le tratan de engañar, le tratan de convencer. Lo agarran de los pies, algunos intentan retenerlo por la fuerza, le golpean.

Pero el hombre esta decidido a escapar, ya lleva mucho tiempo en esa oscura caverna, sus días se han vuelto insoportables y su vida miserable. Antes no era así, alguna vez estuvo feliz en las catacumbas, encantado con el brillo de los diamantes y las vetas de oro, engañado por los demonios. ¡Pero ya no, ya no más! Los diamantes ya no tienen valor para él, son solo frías e inútiles piedras brillantes, el oro no es mas que ilusiones refulgentes pegadas a la pared. Y los demonios, los demonios son lo peor. Ya no le engañan, ya no los vé altos, hermosos y vistiendo ropas elegantes como los veía al principio, sino tal como son, unos seres encorvados, llenos de verrugas y pústulas despreciables, con unos cuernos deformes, de tamaños variados y en algunos casos carentes de total armonía, casi sin ropa, vistiendo apenas unos deshilachados taparrabos, que alguna vez fueron blancos pero ahora están negros del uso. En general, son seres verdaderamente despreciables, algunos caminan por el techo o por las paredes, en dos patas o en cuatro, es indiferente, algunos hablan en una lengua olvidada hace mucho tiempo y su tono de voz grave en combinación con el sobre uso de monosílabos y la carencia de vocales de su idioma haría temblar al más valiente.

Cada vez el hombre está más cerca. Ha dejado plantados en el piso a varios demonios, y el camino que ha recorrido esta lleno de cadáveres. El olor que dejan es pestilente, y eso que la mayoría no llevan mucho muertos. El pico y el rostro del hombre están llenos de sangre, y su taparrabos está también ennegrecido por el uso. Camina semi-desnudo con un sólo objetivo: llegar hasta la puerta.

En el camino pasa por la entrada a una galería que se abre a la izquierda. La pared del fondo de la galería está cubierta de diamantes. Antes de que pueda reaccionar de la galería se le arroja encima una sombra. Con la velocidad de un rayo caen al piso y ruedan, el trata de rodar hacia la salida, luego se separan y el hombre sigue arrastrandose en dirección a la luz. Hace calor, el hombre suda, tiene sed, tiene hambre, pero su anhelo de libertad es mayor que todo eso. ¡Se arrastra! La sombra se recupera, le toma por los tobillos y le propina un golpe en la espalda que le produce un dolor insoportable. Pierde el aliento, y luego de un esfuerzo logra recuperar la respiración. Se voltea y logra zafarse de las garras del demonio que le retiene. Lo conoce, quizá es el peor de todos, sus garras han lacerado sus tobillos, pero aún así se incorpora y lo golpea. El demonio sangra, curiosamente, a pesar de lo desagradables y fuertes que son, en el fondo su cuerpo sangra y se deshace al menor golpe. La cara se le llena de sangre, está atontado, pero no tardará en recuperarse, es el momento decisivo, el hombre toma el pico y vuelve a golpear. Un golpe seco y el demonio está muerto. Bueno, muerto es un término muy relativo, ellos no mueren en verdad, su cuerpo se deteriora, se pudre y luego encarna de regreso, por eso son pestilentes y son tan fáciles de herir, porque ya están muertos, son muertos en movimiento.

Finalmente está cerca de la salida, el peso que lleva colgado del cuello ahora casi no lo deja moverse, mientras más cerca de la salida es más difícil de llevar, mientras más cerca de la salida más pesado se hace. Está sujeto al cuello con un candado, que es prácticamente imposible de soltar. En el mundo inicial de la ilusión, cuando creía en los demonios, cuando estos le engañaban el peso parecía una joya, y el grillete con el candado al rededor de su cuello parecía una joya, y como todo en esas épocas, era hermosa. Poco a poco se fue evidenciando la verdadera naturaleza del artefacto, poco a poco así como se fue revelando la verdadera identidad de los demonios también se reveló la naturaleza del grillete.

Ya casi llega a la salida. Mira hacia atrás y ve la mina en todo su esplendor, el calor es insoportable, las hogueras de las fundiciones resaltan en la oscuridad, y se pueden ver las sombras de otros demonios y esclavos moverse de un lado a otro, levando piedra, atizando fuego, agitando látigos. El demonio que acaba de matar ya se está regenerando, debe apurarse, porque no tardará mucho en levantarse.

Sigue caminando, está al borde de la salida, da un paso, después otro y finalmente logra salir. La luz del exterior le ciega, el aire fresco le quema los pulmones, y entonces le asalta esa extraña sensación de no saber que hacer, de falta de propósito. Ha pasado tanto tiempo en la mina que tiene dificultades en comprender cual será el siguiente paso. Los demonios no salen de la mina, así que ya no le pueden prender, pero el mundo exterior puede resultar intimidante.

catacumbas2

Entonces se quita su carnet de identificación del cuello y lo guarda en el bolsillo de la chaqueta. Acomoda las carpetas que lleva debajo del brazo, se sube el cuello de la chaqueta y se pierde entre la multitud rumbo a su casa.

¿Cómo descomprimo un WAR? (Humor)

Este es uno de esos episodios graciosos con los que uno se encuentra en la industria de software (algunas veces más frecuentemente de lo que uno quisiera). En este post voy a “sacarle el cuero” a un colega, de modo que por razones evidentes, voy a usar nombres falsos.

out_out

En estos días conversé con un viejo amigo, a quién de ahora en adelante llamaré “Bernardo”, con quién trabajé mucho tiempo y a quién personal y profesionalmente respeto mucho. Mi amigo estaba de visita en la ciudad y me contaba entre muchas otras cosas que se encontraba trabajando a distancia en un proyecto con alguien que se suponía era un “programador profesional en Java”. De ahora en adelante llamaré “Roque” a esta persona. Bernardo me contaba que se sentía un poco frustrado porque afirmaba que Roque, era bastante incompetente, y que era bastante difícil trabajar con alguien así.

El asunto quedó hasta allí y no pasó de ser más que un cuento en nuestra conversación, pero algunos días después Bernardo me envió un SMS con el siguiente texto (he alterado/mejorado un poco el texto, pero el sentido es exactamente el mismo):

Para que veas que no exagero. Copia textual de un correo de Roque: “¿Cómo descomprimo el .war?”

Bien, no todo el mundo sabe lo que es un “.war”, pero, un “programador profesional en Java” que se dedica a hacer aplicaciones web que no sepa lo que es un “.war” ¿WTF?

Al final de todo, un “.war” (Web application Archive) es un vulgar “.zip” con la extensión cambiada a “.war” que contiene algún tipo de aplicación web. Por otro lado, ¿qué tan difícil puede ser abrir Google y escribir algo como “java war“?

A decir verdad, ese día me sentía con ánimos un tanto irónicos y sarcásticos, por lo que le respondí a mi amigo lo siguiente:

Dile que lo intente con Microsoft Office, pero que no te acuerdas exactamente si el asunto es con Excel, con Access o con PowerPoint… ¡Digno de Dilber!

¿Digno de Dilbert?

internet_full_500

¿Ustedes que opinan?

Por cierto, al principio me sorprendió no recibir otro SMS de Bernardo en respuesta a mi alusión a MS Office, pero media hora después recibí lo siguiente:

¡La última media hora ha sido de reir/llorar!

Impresiones sobre el taller de UML en el IUTET

El 16 y 17 de Noviembre del 2012 tuve el gusto de dictar un taller de UML a un grupo de profesores del IUTET (Instituto Universitario de Tecnología del Estado Trujillo).

UML, que significa en Inglés Unified Modeling Language (Lenguaje Unificado de Modelado en Español), es un lenguaje gráfico que se utiliza para modelar y representar aspectos técnicos y no técnicos de un sistema de software. Éste lenguaje, en esencia, permite que los desarrolladores de software puedan comunicar y documentar ideas y aspectos de diseño de un sistema de software.

El taller de UML, al que por cierto me gusta titular “UML Ilustrado”, principalmente por la cantidad de ejemplos que incorpora y su visión netamente práctica, es parte del trabajo de capacitación que venimos realizando dentro del contexto de Evolución Ágil, que es un proyecto / empresa en el que he estado trabajando desde finales del año pasado junto con Pablo Lischinsky y Elysabeth Guevara, y que tiene como objetivo brindar capacitación y consultoría en marcos de trabajo ágiles para la gestión de proyectos de desarrollo de software u otros productos.

UMLSlides_01_th UMLSlides_02_th UMLSlides_03_th

Ahora bien, dictar un curso de UML dentro de la concepción ágil, es un trabajo un tanto difícil. Esto se debe, principalmente a dos mitos asociados con la agilidad: 1) En el desarrollo ágil no se documenta (realidad: si se documenta) y 2) En el desarrollo ágil no se usa UML (realidad: si se puede usar UML, siempre y cuando se use de forma adecuada). A esto se le suma que si bien UML se tiende a asociar a procesos de desarrollo mucho más pesados tales como RUP, lo que hace que la audiencia que usualmente asiste a este tipo de talleres tienda a estar algo sesgada hacia procesos muy orientados al Waterfall y a una visión Big Design Up Front.

Todo esto me preocupaba un poco al momento de preparar e iniciar el taller, sobre todo porque en Evolución Ágil estamos decididos a mantener nuestro compromiso con la agilidad, pero para mi sorpresa, este sesgo no fue un impedimento en lo absoluto para introducir ideas y valores básicos de agilidad, sino todo lo contrario, fue una oportunidad increible para discutir, por contraste y utilizando UML como centro de todo, aspectos y valores importantes asociados a la agilidad.

Entre otras cosas, fue posible poner UML en su correcto lugar, es decir, no como la panacea que resuelve todos los problemas del diseño/desarrollo de software, que es lamentablemente lo que muchos desarrolladores aún hoy en día piensan, sino como una herramienta más de la caja de herramientas que tenemos a nuestra disposición y que bien utilizada puede ayudarnos a hacer mejor nuestro trabajo.

UMLTool_01_th UMLTool_02_th

Por ejemplo, el taller hizo mucho énfasis en ideas como las que propone Martin Fowler en su artículo “Is Design Dead?”, sobre todo en la sección titulada “UML and XP”, en las que se plantean cosas como que hay que hacer diagramas de UML cuando realmente son útiles, como forma de comunicar ciertas ideas, y que hay que invertir tiempo en mantener los diagramas actualizados siempre y cuando el equipo de desarrollo realmente los esté utilizando (y botarlos a la basura si el equipo de desarrollo no los usa). También pude introducir el concepto de “UML-itis”, que es básicamente esa equivocada necesidad que tienen muchos desarrolladores de hacer diagramas y más diagramas aún en situaciones en las que éstos no son necesarios.

UMLitis_01_th UMLitis_02_th

La recepción en el IUTET fue excelente, creo en verdad que los participantes del taller supieron aprovechar muchisimo el contenido, hubo mucha discusión y mucha interacción. Pocas veces como instructor se tiene una audiencia tan atenta e interesada como la que tuve en el IUTET, y todo esto, claro está, hace que dictar un taller sea muy agradable.

UML_grupo_IUTET_th

Finalmente, quisiera agradecer por la oportunidad y por el recibimiento a Doris Briceño y a José Quintero, ambos profesores del IUTET y que fueron mis anfitriones en Valera, así como también a Jose Mogollon por haberme ayudado a hacer el contacto. Espero en verdad regresar y poder tener nuevamente la oportunidad de ir al IUTET a dictar nuevamente éste u otro taller.

Edit:

La Profesora Doris Briceño muy gentilmente me envió la nota de prensa del curso:

curso_uml_th

RE: Academia e Ingeniería de Software

Nicolas Paez ha escrito un breve post sobre su percepción respecto a la forma en que se enseña Ingeniería de Software en distintas universidades en Latinoamérica. En lo personal, no he realizado investigación alguna al respecto en otras universidades (al menos no formalmente), y cualquier cosa que pueda decir se suscribe por lo pronto, únicamente a mi experiencia y a la forma en que enseñamos Ingeniería de Software en la Universidad de los Andes (ULA), en Venezuela.

En nuestro caso, estamos en el extremo de dictar una sola materia de Ingeniería de Software para toda la carrera. El programa de la asignatura se resume de forma muy general en la siguiente figura:

re_nicolas_map_is

El curso tiene una carga horaria de 6 horas de clase a la semana, de las cuales 4 son “teóricas” y 2 son “prácticas” y se dicta en un total de 16 semanas. Como ven, son 16 semanas en las que si nos remitimos al programa hay que cubrir bastante contenido (debo decir que es un curso bastante agotador, sobre todo si se quiere dictar bien).

Personalmente estoy un poco inconforme con el hecho de que haya una sola asignatura de Ingeniería de Software, y sinceramente creo que la carrera podría beneficiarse mucho si se separase la asignatura al menos en otras dos: Arquitectura de Software por un lado e Ingeniería de Software por el otro. Esto liberaría un poco la presión y el exceso de contenido que tiene la materia actualmente y permitiría profundizar un poco más en algunos temas, y en especial, en algunos aspectos prácticos.

Como se puede apreciar, desde el punto de vista del post de Nicolas, en la ULA estamos mucho más cerca del grupo de “una materia por carrera” (como en la UNQ) que del grupo de “varias materias por carrera” (como es en el caso de la UBA).

Sin embargo, es importante decir que en la ULA ofrecemos una carrera de de Ingeniería en Sistemas, y no una carrera de Ingeniería Informática como sucede en la UBA. La Ingeniería en Sistemas es… bueno, una definición de qué es la Ingeniería en Sistemas (y más importante aún, qué no es) merece otro post completo, pero hay que decir que no sólo nos dedicamos a desarrollar software, sino que también hacemos otras cosas. Somos una escuela formada por tres departamentos (Control, Computación e Investigación de Operaciones) en la que se dicta un extraño híbrido entre varios de los perfiles en computación de la IEEE-ACM, mezclado con Control de Procesos, un poco de Automatización, etc.

Mi visión concreta respecto a lo que plantea Nicolas es muy simple: En la primera clase siempre le muestro a mis estudiantes esta transparencia:

re_nicolas_clase_01_presentacion

Y posiblemente el punto debería ser aún más pequeño de lo que en actualmente es. La pregunta ¿qué representa el rectángulo verde y el punto negro? usualmente flota en el aire por un rato, y hasta ahora, nunca he conseguido que alguien, más que responder, adivine la intención oculta detrás de la interrogante (aunque muchos estudiantes lo han intentado).

La respuesta es simple: El rectángulo representa todo lo que se puede saber sobre Ingeniería/Desarrollo de Software, el punto negro, lo que podemos cubrir en este curso. La idea fundamental detrás de esta pregunta es mostrar lo verdaderamente abismal de todo lo que en efecto NO se puede cubrir durante el curso.

La lámina anterior resume lo que pienso sobre la enseñanza de la Ingeniería de Software hoy en día: Simplemente no podemos enseñar/transmitir a los estudiantes todos los conocimientos teóricos que necesitan para afrontar todos los problemas que se van a encontrar en sus carreras profesionales. Lo que si podemos (y debemos) es formar criterio, suficiente para que nuestros estudiantes puedan aprender luego por sí mismos y enfrentar nuevas situaciones según sea el caso. Esto es particularmente cierto en una ingeniería tan dinámica como la nuestra, y también tan subjetiva, en la que muchas cosas y conceptos dependen de la dirección desde la que se las vea, o tal como lo dice Martin Fowler, refiriéndose en este caso puntual al término “arquitecto” y luego generalizándolo casi a cualquier cosa:

“In software, the term architect means many things. (In software any term means many things)”

…o peor aún, como me gusta decir a mis estudiantes, en Ingeniería de Software, dados dos puntos de vista diametralmente opuestos, simplemente “escojan un bando, pero eso si, argumenten bien el porqué de la elección”.

Finalmente, me gustaría también aportar un poco más que sólo el feedback que solicitó Nicolas en su post. Me gustaría hablar sobre las distintas formas en que hasta los momentos he dictado la asignatura.

No se hasta que punto mis estudiantes se dan cuenta de esto, pero he hecho unos cuantos experimentos durante las distintas instancias del curso a lo largo de los últimos cuatro años. Algunos han salido bien, otros muy bien, y otros casi se convierten en desastres totales (bueno, quizá no tanto). Simplificando un poco, he dictado la materia usando tres enfoques distintos: el teórico clásico (y aburrido) en el que se dicta un curso lleno de contenido, el práctico utilizando cascada y el práctico utilizando métodos ágiles. También he integrado el curso de Ingeniería de Software con el curso de Bases de Datos, y hasta hay un artículo que está por publicarse al respecto, pero eso ya es harina de otro costal.

Con el enfoque ágil, he usado distintos sabores de Scrum (ScrumBut dirán algunos, y probablemente tendrán razón), tratando más que nada de adaptar Scrum al contexto “muy a medio tiempo” de un curso de Ingeniería de Software, en el que los estudiantes son compartidos generalmente con otras cuatro materias, además de otros obstáculos que no vienen al caso. En este sentido he hecho algunos avances importantes y voy refinando la técnica semestre a semestre.

Sobre el punto de vista “teórico versus práctico”, para mi la respuesta es evidente: la visión práctica siempre gana. Tengo la firme creencia de que la Ingeniería de Software es algo que se debe enseñar/aprender de forma práctica, de modo que eso es exactamente lo que hago con mis estudiantes de pre-grado: Utilizar la estrategia RAIS para desarrollar productos. Además, de quedarme solamente con el enfoque teórico, es muy probable que me hubiese aburrido hace rato, después de todo, no hay nada mejor que hacer productos interesantes en el salón de clase.

El otro punto de contraste es ágil versus cascada. Aquí debo decir que me ha ido bien con ambos enfoques, pero me ha ido mucho mejor con el enfoque ágil, que es el que utilizo actualmente. Desde el punto de vista académico creo que las dos visiones tienen ventajas y desventajas. Ágil por un lado permite comenzar a desarrollar casi desde el día uno, cascada no. Por lo antes mencionado, ágil permite desarrollar productos más ambiciosos que con cascada, porque usualmente hay más tiempo dedicado a la programación, a aprender las nuevas tecnologías y técnicas necesarias para desarrollar el producto de turno, etc.

Ahora bien, dependiendo del punto de vista, una de las desventajas de ágil es que pareciera que permite ejercitar menos algunos aspectos más formales del contenido del curso, cosa que si es posible hacer con cascada… quizá ha sido una falla mía, pero por lo pronto esa es la impresión que tengo. Por otra parte, ágil permite hacer algo que considero bueno: ejercitar algunos valores y principios fundamentales para desarrollar software, y con los que particularmente me siento muy identificado, así como enseñar la importancia de generar productos funcionando en lugar de producir pilas de papeles (usualmente inútiles desde mi punto de vista).

En resumen, y por los momentos, hasta ahora me quedo con la visión ágil en mis cursos de Ingeniería de Software.