viernes, 22 de diciembre de 2017

Reflexiones sobre las Metodologías Agiles

En estos últimos años, se ha ido extendiendo el modelo de desarrollo software llamado Metodología Agile o desarrollo ágil de software, en sus distintas variantes y enfoques: Kanban , Scrum o XP entre otros. Aunque muchos principios son antiguos, y algunos como la Programación Extrema (XP) se pusieron de moda hace años, ha sido últimamente cuando se ha desatado el “furor” por este modo de trabajo.

En el Manifiesto por el Desarrollo Ágil se resaltaron una serie de criterios y ponderaciones
  • Individuos e interacciones sobre procesos y herramientas
  • Software funcionando sobre documentación extensiva
  • Colaboración con el cliente sobre negociación contractual
  • Respuesta ante el cambio sobre seguir un plan
Que se plasman en 12 principios:
  1. Nuestra mayor prioridad es satisfacer al cliente mediante la entrega temprana y continua de software con valor.
  2. Aceptamos que los requisitos cambien, incluso en etapas tardías del desarrollo. Los procesos Ágiles aprovechan el cambio para proporcionar ventaja competitiva al cliente.
  3. Entregamos software funcional frecuentemente, entre dos semanas y dos meses, con preferencia al periodo de tiempo más corto posible.
  4. Los responsables de negocio y los desarrolladores trabajamos juntos de forma cotidiana durante todo el proyecto.
  5. Los proyectos se desarrollan en torno a individuos motivados. Hay que darles el entorno y el apoyo que necesitan, y confiarles la ejecución del trabajo.
  6. El método más eficiente y efectivo de comunicar información al equipo de desarrollo y entre sus miembros es la conversación cara a cara.
  7. El software funcionando es la medida principal de progreso.
  8. Los procesos Ágiles promueven el desarrollo sostenible. Los promotores, desarrolladores y usuarios debemos ser capaces de mantener un ritmo constante de forma indefinida.
  9. La atención continua a la excelencia técnica y al buen diseño mejora la Agilidad.
  10. La simplicidad, o el arte de maximizar la cantidad de trabajo no realizado, es esencial.
  11. Las mejores arquitecturas, requisitos y diseños emergen de equipos auto-organizados.
  12. A intervalos regulares el equipo reflexiona sobre cómo ser más efectivo para a continuación ajustar y perfeccionar su comportamiento en consecuencia.
By Dr ian mitchell (Own work) [CC BY-SA 3.0]

De forma muy simplificada, ¿qué se pretende conseguir?

Pues que en lugar de acordar con un cliente (interno o externo) un proyecto totalmente cerrado, que el cliente no ve hasta que han pasado muchos meses, descubriendo (con horror en algunos casos) en el momento de la entrega que no tiene nada que ver con lo que pensaba, se utiliza una aproximación gradual que el cliente/usuario puede ir siguiendo y orientando.

En lugar de un modelo jerárquico donde un jefe de proyecto o un analista transmite unas decisiones (que en algunos casos pueden ser erróneas), se utiliza un modelo cooperativo de trabajo en equipo, donde todo el mundo (incluido el usuario) aporta y se siente integrado, consiguiendo más calidad y productividad.

Y en lugar de perder demasiado tiempo documentando y actualizando documentación en algunos casos innecesaria, debe emplearse en hacer un código fuente claro y autodocumentado.

Personalmente pienso que el trabajo en equipo y la implicación de todos integrantes de un proyecto es un principio indiscutible, se use la metodología que se use. El tener en cuenta las opiniones de profesionales con distintos enfoques (programador, arquitecto, experto en base de datos, administrador,..) enriquece y permite detectar problemas y orientar mejor el proyecto, teniendo en cuenta la experiencia de cada perfil.

Igualmente la participación del cliente/usuario, evita un efecto “teléfono estropeado” donde el usuario cuenta lo que quiere a un técnico, que lo traduce para otro que lo transmite a otro, hasta que al final el programador implementa algo totalmente distinto.



También parece indiscutible, se use la metodología que se use, la efectividad de revisar el trabajo y el modo de desarrollo, buscando y aplicando mejoras al mismo (tanto a las soluciones como al modo de trabajo), lo que lleva años aplicándose en cadenas de montaje industriales de Japón con el nombre de Kaizen.

Sin embargo hay otros principios que aunque inicialmente parecen muy razonables, sin embargo pueden provocar problemas. Como en cualquier herramienta, disciplina o metodología, los problemas provienen de dos fuentes: mal uso de la metodología y problemas intrínsecos a la metodología, que en algunos casos se entremezclan.

Algunos problemas potenciales


Quizá el primero que surge es el de coste y plazos. Antes de empezar un proyecto debe conocerse su viabilidad y fechas de disponibilidad. Si el alcance (requisitos) y la solución no están muy cerrados no es posible conocer si el proyecto es viable ni si podrá entrar en producción en plazos. Por tanto, o se conocen (casi) todos los requisitos inicialmente (lo que choca con la “alegría” en “recibir requisitos en épocas tardías”), o son requisitos menores o se corre el riesgo de irse en plazos y costes tanto o más que en desarrollo “tradicional”.

Hay otro riesgo relacionado con el suministro gradual de requisitos. Como se citaba en el párrafo anterior, hay requisitos que pueden implicar más trabajo, pero hay requisitos que pueden implicar cambios sustanciales en la solución o herramientas usadas. Hace años las aplicaciones se desarrollaban en un lenguaje de programación u otro, almacenaban los datos en una base de datos y el grueso del trabajo era “programación” sin más. Pero los proyectos son cada vez más complejos, se exigen más funcionalidades, y los volúmenes son mayores. Esto ha provocado que raramente se desarrolle absolutamente todo desde cero. Lo habitual es que se utilicen framework de desarrollo, que se utilicen muchas librerías o bibliotecas  y que se integre múltiples productos y servidores en un solo proyecto. Los requisitos de “última hora” pueden provocar que se descubra que alguno de los lenguajes/productos/librerías/tecnologías elegidos no sirven porque no cubren esa función o no ofrecen el rendimiento esperado, lo que puede provocar:
  • el rehacer gran parte del desarrollo para integrar otro producto/librería,
  • el tener que comprar licencias de otro producto,
  • el requerir infraestructura y servidores que no estaban previstos,
Todo ello con los costes económicos y de plazos asociados.

Otro punto potencialmente peligroso es el de las “entregas periódicas”. Un proyecto medianamente complejo requiere normalmente la construcción (o selección) de marcos de trabajo, librerías, clases, etc. comunes a todo el proyecto y que luego facilitan el desarrollo y su calidad. Pero esos elementos iniciales en la mayoría de los casos son componentes muy “técnicos”, no son entregas “funcionales” por lo que puede ocurrir que los Sprint iniciales, o bien se hacen mucho más largos que el resto o bien la “revisión del proyecto” puede ser ver un fichero jar de java, que “no muestra nada” inteligible.

Quiero con ello resaltar la importancia de estas primeras fases en que se pone los cimientos y estructura del proyecto, y durante los cuales no hay nada que mostrar a un usuario no técnico. El forzar un desarrollo que muestre periódicamente “algo funcionando” puede llevar a desarrollos mal estructurados y mal enfocados, provocando el clásico desarrollo “espagueti” de Visual Basic que aunque muestra rápidamente pantallas, se hace imposible de mantener y propenso a errores al tener “todo conectado con todo”.

Finalmente, respecto la documentación, hay dos elementos “peligrosos”:
  • se debe dar prioridad a “software funcionando frente a documentación”.
  • “El método más eficiente y efectivo de comunicar información es la conversación…”,
Respecto al primero, si la documentación se toma como un “trámite” que se encarga al “becario”, el proyecto está condenado al fracaso a largo plazo (con cualquier metodología) ya que la documentación debe reflejar una visión clara y de alto nivel del proyecto de forma que en un futuro se entienda cómo funciona todo el sistema, qué se integra con qué, y porque se han tomado ciertas decisiones y no otras.

Desde luego documentar lo que un método o función hacen, puede documentarse en el Javadoc del mismo, no tiene sentido elaborar una documentación redundante y con riesgo de incoherencias o desactualización.

Pero muchas veces la documentación más importante NO está en el código fuente (entre otras cosas porque puede afectar a varios fuentes, a la forma de instalar un producto o de usar una librería o a otros elementos que no son el código fuente en sí). La documentación debe hacer referencia a la arquitectura general, la forma recomendada de uso, documentar porqué se optó por un producto o implementación. Sin esa información documentada adecuadamente, una segunda fase del proyecto (o la corrección de errores), que además podría incorporar un equipo nuevo, puede tomar decisiones incorrectas.

Respecto a la transmisión de información oral, evidentemente ayuda a explicar algunos aspectos y a debatir propuestas en modo “tormenta de ideas”, pero el escribirlas ayuda a formalizar y sistematizar las ideas y propuestas y formaliza el análisis y debate de los puntos, generando reuniones más eficaces. “Las palabras se las lleva el viendo”, y adicionalmente el texto escrito y formal permite evitar interpretaciones diferentes por distintos integrantes de un equipo.

En función de las reflexiones anteriores, podría deducirse que para proyectos donde el grueso está decidido (o incluso implementado) y queda pendiente el interfaz de usuario, o se trabaja sobre una nueva iteración, parece especialmente adecuado, mientras que para proyectos totalmente nuevos, con mucha indefinición funcional y tecnológica, el riesgo con una metodología ágil es mayor, especialmente si no se utiliza adecuadamente.

Como en cualquier herramienta (física o intelectual) debe utilizarse con criterio y analizando en cada caso los riesgos y necesidades. Al final, entre el blanco y el negro hay toda una gama de grises, y se debe elegir cual es el “color” adecuado, aplicando la metodología y los criterios de la forma adecuada y sin “fundamentalismos”.

No Brain | by Pierre-Olivier


Como indicaba un letrero sobre una maquinaria pesada “CAUTION: This machine has no brain, use your own” (“Precaución: esta máquina no tiene cerebro, use el suyo propio”)

domingo, 22 de octubre de 2017

Los Microservicios NO son LA SOLUCIÓN

 

No, los Microservicios NO son "LA SOLUCIÓN" ni "valen para todo".


Estos dos últimos años se ha desatado una “locura” en torno a la Arquitectura de Microservicios, que ha “revolucionado” el modo de desarrollar y aparenta ser el “Bálsamo de Fierabrás” que “cura todas las dolencias”. Cualquier proyecto que no utilice Microservicios se considera automáticamente una “antigualla” y totalmente obsoleto  y erróneo. Me temo que debo disentir.

Como cualquier solución tecnológica, tiene sus ventajas e inconvenientes y sus ámbitos de aplicación.  Hay muchos escenarios para los que es una solución válida, pero no es aplicable automáticamente a todos los proyectos, ni siquiera a la mayoría, y además creo que el “entusiasmo” está haciendo no se evalúe adecuadamente sus desventajas, solo las ventajas. Para cualquier proyecto y escenario debe evaluarse con la mente abierta diversas soluciones, ponderar y medir ventajas e inconvenientes y sólo entonces elegir la mejor opción.

Creo que algunos sentimos una sensación de “Déjà vu” que hace recordar en cierto modo el entusiasmo en su momento con los Servicios Web, ahora rechazados frente al nuevo “juguete”. Ambos coinciden en ser una Arquitectura Orientada a Servicios, que prometía desacoplar los elementos de las aplicaciones, desarrollar aplicaciones más ligeras sin repetir código, desarrollar en distintos lenguajes y tecnologías y ofrecer un abanico servicios que por medio de una especie de diccionario/directorio, permitiría a las aplicaciones dinámicamente localizar el servicio web adecuado e invocarlo. ¿Suena conocido?

Antes de reflexionar sobre la Arquitectura de Microservicios, hay que destacar que su aparición ha coincidido con dos tecnologías/paradigmas muy importantes y útiles, un incremento de las posibilidades y oferta de virtualización (por medio de servicios como Amazon o Azure o por medio de “micro-máquinas virtuales” como Docker) y los modelos de integración continua y DevOps. Hay que tener en cuenta, que puede usarse microservicios sin esos elementos y que puede hacerse desarrollo “monolítico” con ellos, por tanto hay que centrarse exclusivamente en las características de los microservicios en sí mismos.

Otro punto previo a considerar es la simplificación planteada de “aplicación monolítica” frente a “aplicación basada en microservicios”. Creo que hace muchos años que no veo una aplicación totalmente monolítica y aislada. La mayor parte de las aplicaciones están distribuidas en varios servidores, invocan Servicios Web, leen de colas de mensajería, se integran con herramientas EAI, intercambian archivos con otros servidores del mismo proceso, etc. En cuanto a los microservicios, a pesar de su nombre no son exactamente “micro”. Según recomiendan los expertos, deben tener cierta coherencia funcional, por lo que no se trata simplemente de, por ejemplo, un servicio de “alta de un elemento”, ni siquiera de un servicio de mantenimiento (alta, baja y modificación) de elementos. Cada “servicio” puede consistir en varios métodos/funciones de diversa complejidad. Por tanto no se trata de comparar opción “monolítica - gigante” frente a opción “totalmente distribuida - micro” sino opción “poco distribuida con grandes bloques” frente a opción “muy distribuida con bloques pequeños”.

Para poder comparar ventajas e inconvenientes voy a plantear un proyecto hipotético a desarrollar en Java para desplegar en un servidor de aplicaciones, comparando las dos alternativas de arquitectura (monolítica vs microservicios) y revisando hasta qué punto son reales las “ventajas” de los MicroServicios. Supongamos un proyecto que consta de 4 bloques funcionales (susceptibles de convertirse cada uno en un micro servicio) con interrelaciones entre ellos. Para este escenario, se comparan las dos soluciones en tres aspectos principales:
  1. Diseño - Desarrollo,
  2. Despliegue - Infraestructura
  3. Explotación - Monitorización.

 

1- Diseño - Desarrollo.


 1-A) “Los microservicios permiten a trabajar a varios grupos de forma independientemente”.
Por supuesto, al igual que los paquetes, espacios de nombres, librerías o diversas técnicas existentes en la mayoría de los lenguajes ”serios”.
Dado que los métodos y parámetros entre bloques/servicios deben estar definidos en fases iniciales (en otro caso no podemos saber a qué se invoca ni con qué parámetros ya sea por medio de microservicios o métodos de clases) los 4 bloques funcionales podrían dividirse en 4 proyectos/paquetes/jar y trabajar en paralelo cuatro equipos, exactamente igual que 4 equipos podrían trabajar en los 4 microservicios. La diferencia principal sería que unos invocarían a otros llamando a métodos y clases y otros invocando microservicios remotos.

1-B) “Los microservicios permiten a trabajar en diversos lenguajes y tecnologías”.
Eso es cierto, aunque relativamente. Si necesitas invocar a otras tecnologías (ejemplo desde Java a servicios .Net) siempre puede utilizarse invocaciones REST, servicios Web o incluso JNI (https://es.wikipedia.org/wiki/Java_Native_Interface), aunque desde luego no siempre están disponibles todos los productos en esa forma. Además, para los principales lenguajes hay miles de librerías y APIs de productos.
Por último, el tener un equipo especializado en diversas tecnologías/lenguajes implica que si hay que mover técnicos de un servicio/bloque a otro puede no ser posible. Un equipo uniforme es más elástico.

1-C) “Los microservicios permiten tener BB.DD independientes para cada microservicio”.
Tradicionalmente las BB.DD. han incluido dos características vitales: La transaccionalidad (que nos permite realizar una serie de operaciones y, si todo es correcto, confirmarlas de forma que se haga todo o nada) y la integridad relacional (que asegura que no pueda referenciarse valores o códigos que no existen). Cuando se utiliza una BB.DD. con un modelo “completo”, la transaccionalidad e integridad está asegurada.
Si cada conjunto de tablas está separado y las operaciones se realizan invocando servicios, deberá gestionarse manualmente escenarios como que tras invocar una actualización o inserción de varias tablas, cuando uno de los servicios notifique que no se ha podido realizar la operación, deba deshacerse la operación. Para ello deberá implementarse en cada servicio una serie de métodos para deshacer cada una de las posibles operaciones del servicio (es decir, grosso modo se multiplica los métodos/servicios expuestos por 2).
Similarmente, debe gestionarse la integridad, es decir asegurar que cuando se inserta un registro, los valores referenciados (códigos de país, producto, cliente,…) existen o que antes de borrar se compruebe que nadie lo referencia. Esto no es trivial ya que, en situaciones de mucha concurrencia, entre la comprobación de existencia y la inserción, otra tarea puede borrar el registro referenciado, generando inconsistencias.

Veamos ahora los inconvenientes durante el desarrollo que en ocasiones se olvidan de los microservicios:

1-D) Control de tipos de parámetros: Cuando la comunicación entre bloques se hace utilizando un lenguaje (Java en este ejemplo), los cambios o control de tipo de dato (cadena, fecha, entero, etc.) se detectan en tiempo de compilación y son explícitos en la propia definición de los método y clases publicados. En los microservicios el intercambio de datos se hace en modo texto, por lo que debe convertirse todos los parámetros a texto y volverse a convertir al tipo físico al recibirlos. Esto no solo implica una sobrecarga de desarrollo sino un riesgo de errores, al poder depender de elementos locales a cada servidor como código de páginas (UTF-Windows 1252), formatos de fechas (dd/mm/aaaa, mm-dd-aaaa,…), decimales (123,4 – 123.4), etc.

1-E) Encapsulación: Cuando la comunicación entre bloques se hace utilizando un lenguaje (Java en este ejemplo), podemos recibir un objeto de un bloque/método, y pasarlo a otro sin necesidad de conocer la estructura, ya que la orientación a objetos nos asegura la encapsulación. Por ejemplo el bloque funcional de Contratación puede devolver en un método un objeto de clase Pedido y ese objeto pedido se transmite completo al bloque Almacen: Almacen.ActualizaInventario(Pedido1). Si se cambia la estructura (y el modelo de datos) de la clase Pedido, solo debe modificarse los componentes realmente afectados, no los intermedios. Con un modelo de MicroServicios, muchos elementos intermedios tendrían que modificarse para transmitir o leer todos los elementos de un Pedido. Esta encapsulación también afecta a otros aspectos de implementación y rendimiento. Por ejemplo, una invocación a un método Pedido.getItems() que devuelva la lista de elementos, puede implementarse para que devuelva a lista desde memoria (si estaban cargados previamente) o busque en la BB.DD. si no lo estaban). Con microservicios sería necesaria la invocación explícita al otro microservicio.

1-F) Sobrecarga de comunicaciones: La invocación a una clase o método dentro de un lenguaje es trivial. La invocación a otro microservicio, implica un código adicional (ya sea implementado directamente o utilizando un framework) que debe gestionar la conversión y emparejamiento de parámetros, la gestión de comunicaciones y timeout, reintentos, resolución de urls, etc.

1-G) Control de parámetros: Cuando la comunicación entre bloques se hace utilizando un lenguaje, si un método de un bloque funcional que admitía 2 parámetros se modifica para utilizar 3, esos cambios se detectan y saltan en tiempo de compilación. En el caso de los microservicios, hasta que no se invoque no se detecta el error.

2- Despliegue e infraestructura.


2-A) “Los microservicios son más ligeros”
Si repasamos las necesidades (obviando la alta disponibilidad, que implica duplicar todo en cualquiera de las dos arquitecturas), vemos que la aplicación monolítica requiere 1 sistema operativo + 1 máquina Java + servidor de aplicaciones + librerías comunes y de utilidad (driver jdbc, log4j, manejo XML, etc.) + cuatro módulos, lo que puede representarse como:

Bloque/Servicio A
Bloque/Servicio B
Bloque/Servicio C
Bloque/Servicio D
Librerías comunes
Servidor Aplicaciones
JVM
Sistema Operativo

La versión basada en microservicios requerirá 4 * [ 1 sistema operativo + 1 máquina Java + servidor de aplicaciones + librerías comunes y de utilidad (driver jdbc, log4j, manejo XML, etc.)+ 1 módulo + elementos adicionales comentados (comunicaciones, formateo y comprobación datos entrada/salida, cancelación transacciones)  ], por lo que podría representarse:

Bloque/Servicio A
Bloque/Servicio B
Bloque/Servicio C
Bloque/Servicio D
Librerías comunes
Librerías comunes
Librerías comunes
Librerías comunes
Servidor Aplicaciones
Servidor Aplicaciones
Servidor Aplicaciones
Servidor Aplicaciones
JVM
JVM
JVM
JVM
Sistema Operativo
Sistema Operativo
Sistema Operativo
Sistema Operativo

Es decir, en el caso muy simplificado de que todos los bloques fueran iguales (caso no real, ya que el S.O., JVM y servidor de aplicaciones son mucho más grandes que los bloques funcionales) se trata de 8 bloques/unidades de memoria (y puede equipararse a consumo de CPU para procesar y manejar esa información), frente a 24 bloques, es decir 3 veces más de infraestructura necesaria (en memoria y CPU).
Si estuviéramos hablando de, por ejemplo 10 microservicios/bloques funcionales, la comparación sería de 14 frente a 60 “unidades”.

Y si consideramos tamaños diferentes de cada bloque y un poco más reales de sistema operativo (incluso en sistemas Docker), tamaños de JVM y servidor de aplicaciones (servidores de aplicaciones miniatura como Jetty) frente a tamaño de microservicios, incluso en el caso de 4 bloques, la comparación es de:

Bloque/Servicio A 20M
Bloque/Servicio B 20M
Bloque/Servicio C 20M
Bloque/Servicio D 20M
Librerías comunes 50M
Servidor Aplicaciones 100M
JVM 100M
Sistema Operativo 500M


830M frente a 3160M o en el caso de 10 microservicios, 950M frente a 7.900M es decir un coste en infraestructura entre 4 y 8 veces mayor (y con otros tamaños de S.O y servidor de aplicaciones, incluso 10 o 15 veces mayor).

2-B) “Los microservicios son más fácilmente escalables”

Suele argumentarse que en el caso de los microservicios el escalado es más fácil, ya que puede desplegarse más instancias del servicio más usado. Es decir si el bloque A es invocado más veces que el resto, podría desplegarse dos instancias/instalaciones del microservicio A y una para cada una del resto, es decir algo como:

Bloque/Servicio A
Bloque/Servicio A
Bloque/Servicio B
Bloque/Servicio C
Bloque/Servicio D

Sin entrar en los costes, ya revisados, hay algo sorprendente en la afirmación. Si se despliega un servicio/bloque en un servidor, ese servidor únicamente puede ejecutar esa tarea. Es decir una instalación dimensionada para atender 4 peticiones simultáneas de servicios A, solo podrá atender esas 4 peticiones:


A
A

B
B

C
C

D
D
A
A

B





D
D


Si se recibe 6 peticiones de tipo A o 5 de tipo D, no podrán atenderse

Un servidor donde se haya desplegado todos los paquetes, dimensionado para atender 16 peticiones, atenderá cualquier petición de cualquier tipo (A, B, C o D) hasta su máximo de recursos

A A A B
A A A B
B C C D
D D D D


O lo que de forma doméstica puede expresarse como “en un cajón grande tienes más flexibilidad que en 4 cajones pequeños”. Por supuesto que pueden desplegarse más instancias para atender al servicio A, pero en la opción monolítica no sería necesario, al margen de los costes de infraestructura antes citados.

2-C) “En caso de caída de un microservicio, el resto puede seguir funcionando”
Me sorprende que tras años de existencia de sistemas de tolerancia a fallos, granjas de servidores, tecnologías cluster, alta disponibilidad Activo/Activo y Activo/pasivo, y disponibilidades predicadas del 99, 99% del tiempo, se plantee que se puede “no disponer” de una aplicación. Pero incluso si fuera el caso, plantear que “si se cae un microservicio el resto siguen funcionando” es algo bastante discutible. Si se trata, por ejemplo, de una contratación y no se puede acceder al servicio de “clientes”, o al servicio de “almacén”, o la “pasarela de pago”, no podrá realizarse esa contratación. La aplicación tiene generalmente una coherencia y unos servicios interconectados. Si uno de ellos se cae, probablemente no pueda realizarse la mayor parte de las operaciones, y en muchos casos, ninguna.

2-D) “Puede desplegarse nuevas versiones de los microservicios de forma independiente”
Habría que distinguir lo primero a que llamamos “nuevas versiones”.  Si se trata de desplegar una corrección de un error o una optimización, por supuesto puede hacerse de forma independiente, al igual que puede hacerse también en una aplicación monolítica sustituyendo el fichero java jar correspondiente. Si se trata de una NUEVA versión, que expone un “contrato” nuevo (con diferentes parámetros, un comportamiento distinto, métodos adicionales,..) desde luego deberá desplegarse y probarse de forma conjunta. En otro caso, se está invocando a operaciones cuyo comportamiento es distinto (aunque no lo sea el “contrato” expuesto). El concepto de “pruebas de integración” existe hace mucho años, tanto en aplicaciones monolíticas como en las que no lo son, debido a que en cuanto hay que integrar varios sistemas, existen riesgos y comportamiento no perfectamente documentados (autenticación entre sistemas, códigos de página, tamaños de bloque, límites en cuanto a número de parámetros. Comportamiento ante excepciones, timeout, etc.)

2-E) Comunicaciones: Por último, hay algo que parece olvidarse, la comunicación entre servidores y microservicios tiene un coste de infraestructura, no es gratis. Y si se trata de un entorno en nube, podemos incluso consultar los precios de transferencias internas y externas (https://aws.amazon.com/es/ec2/pricing/on-demand/). Por tanto el uso de microservicios tiene además ese sobrecoste por el hecho de estar intercambiando información entre servidores que no se produce en la opción monolítica.

 

 3- Administración Monitorización


Respecto a este punto, podría revisarse 3 aspectos:

A) Instalación y actualización (de Sistema Operativo, servidor de aplicaciones, utilidades y por supuesto aplicación).
B) Monitorización (debe controlarse el estado de memoria, CPU y disco de los servidores, así como el estado del servidor de aplicaciones y de la aplicación en sí).
C) Depuración y monitorización del proceso/BAM (es decir tanto depurar y analizar problemas de la aplicación como analizar en tiempo real el estado de un proceso de negocio.

3-A) En el primer caso, parece evidente que, de instalar y configurar un servidor (virtual o no) y una aplicación, hemos multiplicado el trabajo por el número de microservicios del proyecto (por supuesto la alta disponibilidad multiplica en ambos casos por dos). Lo mismo aplica para las actualizaciones y parches de seguridad o fixpack de microservicios, librerías o S.O. Desde luego, con plataformas como Docker esto se simplifica, y el número de tareas manuales se reduce, pero esto también aplica a la opción monolítica. El hecho es que sean muchas o pocas las tareas y pruebas de regresión, hay que multiplicarlas por el número de microservicios.

3-B) La monitorización de los servidores y la infraestructura (que siempre es necesaria para controlar problemas de lentitud, memoria o disco) se multiplica igualmente por el número de microservicios desplegados. No me refiero solo a caídas del sistema (que es casi lo más automatizable) sino verificar que los tiempos de respuesta son adecuados, que el nivel de uso de la CPU no sea del 99% o que la memoria libre de RAM o la de almacenamiento estén por encima de unos valores.

3-C) En cuanto a la depuración, desde luego el analizar un problema recurriendo a 4 (o N) ficheros de traza “paralelos” es mucho más complicado que disponer de un solo fichero donde se vea la secuencia y la lógica de las distintas operaciones. En cuanto a la monitorización de procesos, a nivel funcional y de cuadro de mando, también se complica más al contar con 4 (o N) elementos separados.

 

Conclusión.


La conclusión no es que los microservicios no sean una solución válida, la conclusión es que no son LA SOLUCIÓN a aplicar automáticamente en todos los casos ni son siempre la mejor opción.

Son una opción más a aplicar cuando las características del proyecto sean las adecuadas, a comparar en pie de igualdad con muchas otras, incluyendo, por supuesto, un desarrollo “monolítico”.

viernes, 1 de septiembre de 2017

El corazón de las tinieblas


Con este título no quiero hablar hoy sobre la famosa novela de Joseph Konrad  (que por cierto acabo de releer en versión bilingüe, lo que permite disfrutar mucho más la riqueza del inglés de Konrad) y que inspiró la película Apocalipse Now; sino reflexionar en un tono ligero, como suele corresponder tradicionalmente a los medios de comunicación en verano, sobre los interfaces de usuario utilizados últimamente por muchos programadores y personas implicadas en el desarrollo de aplicaciones.

Si comparamos las siguientes imágenes, veremos que todas tienes algo en común, utilizan letras blancas (o claras) sobre un fondo negro.


By Ilya - screenshot making, JetBrains - IDEA Community edition interface [Apache License 2.0] via Wikimedia Commons
By Visual Studio Code: Microsoft Sample code: Mr. Stradivarius (talk · contribs) and Toohool (talk · contribs) Screenshot: Codename Lisa (talk · contribs) [MIT] via Wikimedia Commons
By ZxxZxxZ (Own work) [GPL] via Wikimedia Commons

By Sean Haas (http://code.google.com/p/dreckig-os/) [GPL], via Wikimedia Commons

La diferencia es que unas  tienen unos 30 o 40 años y otras son de este año. 
Hace años, cuando la única posibilidad de visualización eran las primeras pantallas C.R.T.  con fósforo verde, no quedaba más remedio que utilizar letras claras sobre fondo negro (salvo que se quisiera quemar la pantalla y los ojos del usuario).
Sin embargo con el tiempo los colores se fueron aclarando e invirtiendo y hace mucho que lo habitual en los ordenadores es usar letras oscuras (negras) sobre fondo claro (blanco) no solo en los interfaces de sistema (como el explorador de archivos) sino en general en los distintos programas (como los paquetes ofimáticos, herramientas de desarrollo, etc.). Incluso los programas de CAD, que tradicionalmente utilizaban líneas blancas sobre fondo negro, se han ido pasando al uso de fondo claro y línea negra (véase por ejemplo Rhinoceros, aunque clásicos como Autocad se mantengan “irreductibles”).

De los pocos elementos que se han mantenido con ese mismo interfaz (e incluso con las mismas funciones) desde hace 30 años son los shells/intérpretes de comandos para los diferentes sistemas operativos, que han mantenido en general su interfaz de fondo negro y letra clara (con excepciones como algunos emuladores de terminal o XTerm de X-Windows). También puede verse estos tonos oscuros en los proyectos trabajando en COBOL y JCL sobre entornos mainframe.

Uno de los motivos que se ha argumentado tradicionalmente para este cambio es que, cuando se trabaja en un entorno iluminado, como es una oficina o una casa particular, el cambio entre un área luminosa (como es el entorno) y un área oscura (la pantalla) cansa la vista al obligar a ajustar la pupila a la distinta luminosidad. Este mismo efecto de deslumbramiento/cansancio se produce por la noche si se está en un entorno oscuro y se utiliza una pantalla clara (fondo blanco y letra negra), por ello se suele recomendar por la noche ese modo “invertido”. Un ejemplo extremo de esto son las herramientas y programas de observación astronómica (como Skymap o Stellarium)), muchas de las cuales tienen un modo “nocturno” que suele utilizar fondo negro y líneas y texto en rojo, para no deslumbrar cuando se utiliza en absoluta oscuridad.

El hecho es que durante muchos años, para editar un documento o una hoja de cálculo, editar un texto, modelar un proceso o programar en un entorno de desarrollo integrado (IDE), siempre se trabajaba con letras oscuras sobre fondo blanco.

Sin embargo desde hace uno o dos años se han ido extendiendo “las tinieblas” ☺ y es más habitual ver pantallas oscuras, sobre todo entornos de desarrollo como IntelliJ IDEA  o Visual Studio Code. Yo incluso lo he visto usar en programas para editar ficheros de texto (de los habituales para editar grandes ficheros, script o fuentes de programas, sin llegar a abrir un IDE completo). Incluso hay extensiones para utilizar esos temas y colores en otros IDE como Netbeans, que tradicionalmente han mantenido colores claros. 

Hay otro contexto donde es habitual ver esta “oscuridad”, es el uso del sistema de virtualización Docker. Este sistema de virtualización está basado en Linux, y aunque existe una versión para trabajar en Windows, lo hace ejecutando una máquina virtual Linux bajo Windows utilizando el sistema de virtualización VirtualBox. El resultado es que es muy habitual ver intérpretes de línea de ordenes Linux incluso en equipos Windows, para gestionar y arrancar las pequeñas (incluso de 50M) “máquinas virtuales” de Docker.

Dado que el ojo humano no ha cambiado, que para editar documentos se sigue manteniendo el negro sobre blanco, al igual que para los libros (electrónicos o en papel) o en muchos otras herramientas que requieren fijar la vista y concentración durante mucho tiempo, hay que concluir que es una moda específica del mundo de desarrollo.

¿Qué motivos puede tener esa moda para extenderse tanto en el ámbito del desarrollo?

Si tenemos en cuenta que Docker es de una alternativa muy moderna y de futuro, que el desarrollo en Android produce muchos más programas, y en ocasiones más originales y atractivos que el desarrollo para ordenadores de sobremesa y que se trata de entornos y herramientas aparecidos recientemente, parece inevitable concluir que el “lucir” una pantalla oscura, aunque solo sea para editar un fichero XML, muestra ante los compañeros y jefes “que estamos a la moda” y trabajando con las últimas tecnologías.

Lo que hace unos años podía ser símbolo de antigüedad y obsolescencia al observar a alguien trabajando con una pantalla oscura (“fíjate, pobres, trabajan en un terminal con lenguajes anticuados como COBOL”) se ha convertido en símbolo de “distinción” (“fíjate, están trabajando con las herramientas más avanzadas”). Aunque se trata de herramientas, mas sofisticadas que un martillo o llave inglesa pero herramientas al fin y al cabo, de nuevo los colores y estilos de moda como símbolo de distinción y estatus.

Esperemos que los creadores de herramientas, y sobre todo los usuarios de las mismas, “vean la luz” y esta ola de oscuridad se disipe. 
Yo desde luego seguiré trabajando con un entorno claro.

By Moriline (Own work) [CC BY-SA 3.0], via Wikimedia Commons

domingo, 25 de junio de 2017

Lo pequeño es hermoso


Lo pequeño es hermoso” es el título de un libro publicado en 1973 por F Schumacher. Su nombre completo es “Lo pequeño es hermoso: Economía como si la gente importara” y defiende el uso adecuado de los recursos, procurando utilizar opciones sencillas y recursos lo más limitados posibles.
Este criterio, que siempre ha sido válido, se hace cada vez más necesario en el mundo de la tecnología. Parafraseando el título, podría decirse “Lo pequeño es hermoso: TECNOLOGÍA como si la gente importara”.
Si observamos la tecnología que utilizamos habitualmente, veremos que las páginas Web aumentan cada año su tamaño (peso) medio ( http://httparchive.org/index.php ), de forma que aunque las comunicaciones han mejorado mucho y el ancho de banda disponible para descargar una página se ha multiplicado por 1000 en 20 años, el tiempo de espera en algunos casos es el mismo que cuando se utilizaba un modem a 33k.
Algo equivalente puede decirse de las aplicaciones. La mayoría de los móviles actuales, y desde luego cualquier ordenador, tiene mucha más potencia, memoria y velocidad que ordenadores de hace 20 o 30 años (recordemos que una memoria RAM de 4 megas y un disco de 200 Megas eran “un buen equipo”, por no hablar del PC inicial, con 640K y un disco de 10M). Sin embargo, muchas aplicaciones que utilizamos no parece que vayan mucho más rápidas que hace años, e incluso algunas van más lentas claramente. Además, muchas tienen unos tamaños “monstruosos” para lo (poco) que hacen.
En algunos casos se puede decir que las aplicaciones modernas “hacen más” (se revisa la ortografía y gramática en tiempo real, se presentan gráficos de diverso tipo, se ubica la posición y se hacen cálculos en base a la misma, etc.) pero no tanto para justificar esos rendimientos.
Creo que hay varios motivos que podemos agrupar en personales y tecnológicos:

Personales:
Uno importante sería la despreocupación de algunos desarrolladores (“ahora los equipos son rápidos”, “hay memoria suficiente”, “Todo el mundo tiene mucho ancho de banda”), que provoca que no se intente optimizar el código, ahorrar memoria o ahorrar ancho de banda. 
A ello se une la falta de programadores con experiencia, que es otro factor importante. Programadores veteranos que han aprendido trabajando con ordenadores y herramientas con pocos recursos se han acostumbrado a optimizar y ser ahorradores y tienden a diseñar los programas de forma “austera”.  
Si a eso se añade la necesidad de cumplir los plazos y las especificaciones, no de cumplirlas “óptimamente”, se comprende que en muchos casos la eficiencia queda en último lugar.

Tecnológicos:
Muchos de los lenguajes de programación se “encargan de gestionar la memoria” (como ocurre con java) un desarrollador, aunque sepa con total seguridad que ya no necesita un elemento, no puede borrarlo y debe esperar a que “se borre cuando se pueda”. No bastante creo que el principal motivo son la “herramientas de ayuda al desarrollo”, entre las que podríamos incluir el uso de diversas librerías, framework de desarrollo y herramientas para generación de código. Dado que los lenguajes y herramientas de programación no incluyen todas las funciones posibles para todas las necesidades, se suele acudir a librerías de funciones que contiene diversas funciones (manejo de sonido, imágenes, cálculo, gráficos, etc.) que permiten realizar operaciones sin tener que desarrollar desde cero ese código. El problema es que en muchos casos, el utilizar unas pocas funciones implica arrastrar toda la librería de funciones completa. Es decir, necesitamos, por ejemplo convertir una imagen de formato, o recortarla, pero las librerías incluyen funciones para todos los formatos, para girar, recortar, alterar el color, etc.
Con los framework de desarrollo, que a diferencia de las librerías no ofrecen conjuntos de funciones sino esqueletos de desarrollo sobre los que se encaja nuestro proyecto, pasa algo similar. Están planteados para un modelo de trabajo con una serie de relaciones y comportamientos, de los cuales es posible que muchos elementos sobren para el programa que se desea desarrollar. De nuevo se arrastra una enorme estructura y cimientos planteados para un edificio de 5 plantas cuando lo que se desea una casa unifamiliar (o quizá una casa de 5 plantas pero con otra distribución).
Por último, hay muchas herramientas que para facilitar la vida, permiten generar código fuente. pero ese generación, aunque cómoda, al ser genérica no siempre suele ser demasiado eficaz, con lo que se gana en velocidad de desarrollo (y en ocasiones en calidad y normalización) no se gana (o se pierde) en eficiencia.
El resultado es que con bastante frecuencia tenemos una gran cantidad de programas de dimensiones desmesuradas, peleando por los recursos y consumiéndolos inútilmente. Al igual que ocurre con la contaminación, la emisión de un coche no es demasiado significativa, la de miles, sí. Y todos sufrimos ese exceso de consumo de recursos sufriendo programas lentos y enormes. La memoria, o el ancho de banda de comunicaciones, al igual que los recursos naturales, no son infinitos y es mejor usarlos razonablemente.
Los anglosajones utilizan la expresión “Keep It Simple, Stupid” (K.I.S.S.: "Mantenlo simple, estúpido") también expresada como : “Keep It Short and Simple” ("Mantenlo corto y simple") o “Keep It Simple and Safe” ("Mantenlo simple y seguro") que puede considerarse una especie de Navaja de Occam,  pero aplicada, no al análisis o interpretación, sino al diseño y construcción.

Ejemplos prácticos:
Habrá quien piense que esto es algo teórico y que un programa “serio” necesariamente es grande y complejo. Para demostrar que es posible voy a citar algunos ejemplos:
  • Base de datos: HSQLDB es un gestor de base de datos Open Source que además de cumplir todos los requerimientos de una BBDD relacional y ser extremadamente rápida (http://www.h2database.com/html/performance.html), incluye las siguientes funciones:
    • java puro que funciona en cualquier sistema operativo
    • Emula funciones y sintaxis de otras bases de datos
    • Puede trabajar como base de datos en memoria
    • Puede configurarse para trabajar de modo portable
    • Un fichero jar de 1,5 M incluye el servidor de BBDD, el cliente JDBC y un interfaz de usuario gráfico. 
  • Servidores de aplicaciones: Jetty es un servidor de aplicaciones Open Source similar a Tomcat que cumple todas las funciones necesarias como cualuier otro (https://en.wikipedia.org/wiki/Comparison_of_web_server_software) (quizá solo se echa en falta un interfaz de administración avanzado) y que ocupa unos 11M completo (aunque puede embeberse y utilizarse solo el núcleo del servidor empleando prácticamente un tercio).
  • Motor de indexación por texto completo: El proyecto Open Source Lucene es el motor de indexación y búsqueda por texto completo más utilizado en el mundo, tanto directamente como a través de recubrimientos como Solr o ElasticSearch. Su núcleo, que permite indexar y buscar, ocupa solo 5M.
  • Gestor Documental: OpenProdoc es un gestor documental Open Source completo que incluye todas las funciones de cualquier gestor documental, junto con un gestor de tesauros. Dispone de una versión portable, que gracias al uso de las tres herramientas antes citadas, así como a seguir el principio KISS, ocupa 80M completo, lo que para un gestor documental, teniendo en cuenta que incluye servidor de BBDD, servidor de aplicaciones más el propio Gestor, es poco comparado con algunos clientes de gestor documental que ocupan 700M, o instalaciones de servidor de 1G
  • Utilidades Android: El programa Army Knife, contiene en 2,3M las siguientes herramientas:
    • Calculadora científica
    • Linterna
    • Conversor de unidades,
    • Temporizador/cuenta atrás
    • Cronómetro multi-vueltas
    • Brújula
    • Nivel
    • Lupa
    • Espejo
    • Regla (cm/pulgadas)
  • Gestión de imágenes: El programa Irfanview en solo 2M incluye visualización de prácticamente todos los formatos de imágenes existentes, conversión de formatos, manipulación completa de imágenes (profundidad de color, resolución, filtros, etc.), procesos de conversión en lotes, creación de miniaturas, etc.

Colibrí Mdf [GFDL (http://www.gnu.org/copyleft/fdl.html) CC-BY-SA-3.0


La conclusión es que “sí se puede”, por tanto creo que es importante que como usuarios o como desarrolladores o diseñadores sigamos el principio de : “Lo pequeño es hermoso: TECNOLOGÍA como si la gente importara” a la hora de crear herramientas o de utilizarlas. El "medio ambiente tecnológico" y nuestro paciencia o nervios lo agradecerán.