Tarugo4, yo estuve allí

Hace casi casi 4 años, el 25 de octubre de 2015, David nos hacía saber en su newsletter y en su blog que estaba montando una conferencia dedicada a la gente que, como yo, leía sus columnas semanales. Nacía entonces la #TarugoConf.

Cada año a partir de entonces veíamos como las redes se llenaban de comentarios en dos fechas clave, cuando salían las entradas a la venta (que eran reducidas por temas de aforo y mucha gente, incluido un servidor, se quedaba sin poder asistir) y en la fecha del evento, en el que los asistentes salían contentos e inspirados.

Tres ediciones después, la providencia me ha permitido asistir a mi primera Tarugo, y puedo aseguraros que Bonilla no defrauda.

La TarugoConf es un evento que combina tecnología, negocios y un montón de cosas más, con un track de charlas, comida “como para una boda” y muchos espacios y oportunidades para conocer a gente, para desvirtualizar aquellos a los que solamente has visto en redes o para recuperar el contacto con compañeros a los que le has perdido la pista.

En el track de charlas los temas en esta edición han sido:

  • Cómo potenciar la innovación en los equipos utilizando un montón de anécdotas de la industria de los helados.
  • Qué mecanismos podemos implementar desde un punto de vista técnico para mejorar la privacidad de nuestros usuarios en nuestros sistemas.
  • Cómo convertirnos en podcasters, qué técnicas utilizar, qué medios comprar, cómo conseguir audiencia y cómo incluso podríamos ganar dinero con ello.
  • Cómo montar un flujo de trabajo para científicos de datos, cómo integrarlos con el desarrollo de producto y sobre todo, pensar si necesitamos ese rol en nuestros equipos.
  • Cómo hacer productos que vendes a otras empresas, con especial hincapié en cómo incrementar la conversión entre un primer contacto y un contrato firmado.
  • Cómo crear mecanismos de entrega de valor, desde control de versiones a gestión de artefactos a entrega continua utilizando anécdotas de consultoría, administración pública y desarrollo de producto.
  • Cómo crear y mantener un estudio de videojuegos, utilizando anécdotas en el rol de estudio, publicador, editor, y todas las anteriores, y con especial interés en tener ambos lados del cerebro en un estudio, creativo y gestión.
  • Cómo hacer negocios en latinoamérica, qué tener en cuenta, qué mercados están creciendo, cómo conseguir presencia y cómo lograr que te paguen, entre otras.

Cada ponencia, de aproximadamente 45min + preguntas, ha sido tan interesante o más que la anterior, y pese a que hemos estado más de 8h en una sala de cine a nivel personal no se ha hecho largo. Los ponentes han hecho un excelente trabajo condensando estos temas, y todas y cada una de las charlas han tenido un componente práctico, y ha sido sencillo traernos deberes “a casa”.

Los voluntarios y la organización han estado todo el tiempo pendientes de los asistentes, poniendo todo el amor, las ganas y los focos de las cámaras en que todos estuviéramos pasando un buen rato, sintiéndonos como en casa.

Finalmente los patrocinadores han estado especialmente involucrados, con un nivel de interacción con los asistentes diferente, y más allá del típico stand. Un ejemplo de ello han sido los chicos de Libros.com una editorial que publica novelas, ensayo, manuales, y artes gráficas utilizando crowdfunding, y que estaban allí por una parte mostrando el resultado de sus campañas, y por otra parte (más interesante aún) buscando historias que publicar. Un equipo muy cercano del que espero hablar más en el futuro, mientras tanto, si tienes una historia que quieres compartir con ellos aquí te dejo el enlace

Como anécdota personal, he conocido en persona a la gran Nerea Luis, Doctora en IA, y cofundadora de T3chfest, con la que comparto, por casualidades de la vida, apellido, y nos hemos contado batallitas sobre el caos administrativo de un apellido que suena tanto, tanto, a nombre.

Enhorabuena a todo el Equipo Tarugo por un gran evento, por hacernos sentir en familia, por enseñarnos tanto y por inspirarnos.

Libro – The Checklist Manifesto

Hoy hablamos de un libro que me ha tenido enganchado durante varios días y varias horas de vuelo, un ensayo lleno de historias médicas, de aviación, construcción y cocina, sobre un mecanismo que comparte que mejora la seguridad, la eficiencia y, sobre todo, protege y salva vidas. una lista.

Hoy te cuento, querido lector, acerca de The Checklist Manifesto, de Atul Gawande.

Nuestra memoria falla, y no hay nada que podamos hacer al respecto

En nuestro día a día, la complejidad de nuestras obligaciones profesionales ha aumentado exponencialmente, y se espera que podamos mantener bajo control situaciones variables, variaciones, opciones e incertidumbre, y en medio del caos es normal, y común, olvidar cosas o pasarlas por alto.

La memoria tanto a corto como a largo plazo puede fallar, y falla. En una mesa de operaciones o en una cabina de un 747, esto tiene trágicas consecuencias.

La lista: alternativa simple y sofisticada

La manera de mitigar el error, comenta el autor, no es prestar más atención, más medios, más maquinaria o más tecnología, sino un enfoque más simple, tener una lista con puntos críticos, y utilizarla y revisarla de manera continua.

Una lista, en este contexto no es algo que se escribe en 5 minutos, no es un manual de instrucciones, no tiene como objetivo enseñar ni educar, y se basa en la suposición de que será utilizada por profesionales que conocen perfectamente su dominio.

El uso de esta lista puede ser, por una parte en un caso normal (despegar un avión) así como en situaciones excepcionales (motor en llamas). En ambos escenarios, tener una lista de acciones a realizar permite libertad a la persona para poder centrarse en la tarea a mano sin olvidar elementos críticos.

Es importante entonces definir qué es una mala lista. De acuerdo con el autor, una mala lista es poco precisa, demasiado larga y difícil de utilizar.

Por otra parte, una buena lista es práctica, precisa, eficiente, fácil de utilizar en situaciones complejas, y sobre todo, un recordatorio de los pasos críticos e importantes que se pueden pasar por alto independientemente de cuan experimentada sea la persona que las va a utilizar.

Creando nuestras listas

A lo largo del libro, el autor nos da algunas pistas para crear listas efectivas tras aprender de la mano de pilotos de Boeing, y de cómo lo puso en práctica en una mesa de operaciones.

Por una parte, queremos entender especificar qué tipo de lista necesitamos:

  • DO-CONFIRM: En este caso la lista se utiliza como punto de sincronización, las tareas se hacen “de memoria” y llegados a un punto específico (antes de comenzar la operación, antes de entrar en una pista de aterrizaje) se comprueban todos los puntos de manera rápida.
  • READ-DO: En este caso la lista se utiliza como una receta, una serie de pasos que hay que seguir en un orden específico. Un ejemplo de uso de esta lista sería el caso del motor en llamas.

Respecto a la cantidad de puntos , idealmente tendremos entre 5 y 9 elementos (7 más/menos 2), que se considera el límite de la memoria a corto plazo o memoria de trabajo aunque podemos adaptarlas a las necesidades de la misma.

El objetivo de limitar los puntos es que podamos revisar la lista en cuestión de segundos, de tal manera que no se interponga en nuestro trabajo, no olvidemos que una lista muy larga puede provocar que empecemos a “saltarnos” puntos de la misma.

Dichos puntos han de ser claros, específicos, solamente aquellos que consideremos críticos, con un lenguaje simple, y específico del contexto, como hemos mencionado antes, no estamos ante una herramienta de aprendizaje.

El documento ha de ser puesto en práctica para verificar que funciona en el mundo real. Los pilotos utilizan los simuladores de vuelo para ello.

Es necesario asegurarse que la lista funciona tanto en situaciones normales y en caso de una emergencia. En caso de que las listas no funcionen en un entorno de pruebas, se toman notas, se vuelve atrás, se mejoran, y se intenta una y otra vez.

Resumen

A lo largo de unas 200 páginas, el autor cuenta ejemplos de cómo las listas nos permiten no perder de vista puntos críticos del trabajo que hacemos sin limitar nuestra creatividad ni capacidad de reacción, más bien al contrario, establece un balance de libertad y disciplina que nos permite mejorar, ser predecibles y efectivos, especialmente en situaciones extremas.

De momento he empezado a experimentar creando y puliendo listas, cosas que revisar antes de dar por bueno un documento, diseño, análisis, o incluso antes de tener reuniones o de entrevistar a un candidato.

Y tú, para qué utilizas tus listas?

Offboarding

El pasado 1 de Julio, tras pasar cuatro años como parte de la familia Amazon Business, cambié de organización para empezar una nueva aventura como miembro del equipo de ingeniería de Kindle.

Cambiar de organización a nivel corporativo es algo suficientemente complejo que “casi” equivale a cambiar de empresa, con lo cual había expectativas que fijar, información que transferir, tareas que cerrar y procesos que documentar.

En este artículo veremos algunos elementos a tener en cuenta cuando cambiamos de equipos desde el punto de vista del técnico individual, algunos estos elementos serán aplicables también a cuando cambiamos de empresa.

Expectativas

Lo primero, una vez aclaramos con nuestro jefe de nuestra intención para cambiar de destino, es necesario fijar plazos para cerrar frentes abiertos y proyectos en curso.

Si estamos cambiando de empresa, lo habitual es que tengamos un plazo mínimo de notificación estipulado en el contrato, mientras que si estamos cambiando de equipo podemos ser más flexibles.

En general, basándome en mi experiencia, entre tres semanas y un mes debería ser tiempo suficiente para cerrar frentes abiertos y hacer transferencia de conocimientos, lo que nos lleva a…

Listar frentes abiertos

El siguiente paso es abrir una hoja de Excel, Google Spreadsheets, o un fichero de texto y comenzar a escribir una tabla con la información siguiente para todo lo que tengamos en la cabeza:

  • Área (o proyecto)
  • Tarea
  • Estado a fecha de hoy
  • Personas involucradas (aparte de nosotros)
  • Fecha tope
  • Estará completo antes de que dejemos el equipo?
  • Quién se puede hacer cargo cuando no estemos?
  • Qué necesitamos para que esté completo?

Aunque tengamos un equipo perfectamente conexionado con todas las tareas en Jira, TFS, GitLab, Github, Trello, etc. siempre habrá tareas que tenemos en la cabeza, ideas y frentes abiertos que no viven en el sistema de gestión de tareas.

Para aquellas tareas que estén asignadas a nosotros en el sistema, podemos repetir el ejercicio documentando el estado actual y el estado esperado cuando ya no estemos.

Es importante dejar a una persona o punto de contacto a cargo de estas tareas, para ello podemos sugerir alguien en nuestro equipo que tenga especial contexto sobre las mismas, de manera que el seguimiento no se interrumpa.

Notificar a personas involucradas

Tras listar los frentes abiertos y detectar las personas involucradas, es importante notificar a otras personas que trabajan con nosotros que no seguiremos en el equipo, especialmente si somos la persona de contacto de una o más iniciativas, y hacer las conexiones necesarias entre otros equipos y los nuevos interlocutores.

De esta manera nos aseguraremos que los proyectos sigan adelante y estableceremos expectativas claras para las personas que están fuera de nuestro equipo aunque colaboren con nosotros.

Documentar progreso

El siguiente paso, una vez aclarado qué vamos a poder poder terminar y qué no, es dedicar tiempo a documentar, especialmente los proyectos que hemos entregado más recientemente y aquellos que se van a mantener en curso cuando no estemos.

Como ejemplo de elementos que podemos documentar, tenemos:

  • Decisiones técnicas tomadas y por qué las hemos tomado.
  • Puesta en marcha en pruebas y producción de nuestros servicios.
  • Detalles sobre scripts y pequeñas automatizaciones que hayamos hecho.

Transferir conocimiento

Además de la documentación, es muy útil tener una o varias sesiones de transferencia de conocimiento con otros miembros del equipo que vayan a heredar nuestras responsabilidades.

Estas sesiones pueden cubrir, entre otros aspectos:

  • Visión general de los diferentes servicios que componen nuestro sistema
  • Ciclo de vida de peticiones (especialmente útil en una arquitectura de microservicios)
  • Tickets cerrados recientemente, especialmente aquellos de alta prioridad, y causas más comunes.

Estas sesiones, además, podemos grabarlas en vídeo de manera que queden como “documentación” para que otros miembros del equipo puedan recurrir a ellas en caso necesario.

Limpiar

Cambiar de equipo suele ser una buena oportunidad para hacer “borrón y cuenta nueva”, así que podemos aprovechar para renovar equipamiento si es posible.

En cualquier caso es una buena oportunidad para hacer copia de seguridad, y borrar repositorios locales de proyectos antiguos, siempre y cuando dejemos copias de nuestras ramas en repositorios remotos.

Re-asignar cuentas, permisos, credenciales, listas de correos…

Dependiendo del grado de automatización que tengamos, nuestro usuario puede ser administrador o dueño de ciertos recursos, por tanto será necesario re-asignar dichos recursos así como rotar claves en caso de que no tengamos un sistema automatizado. En caso contrario se podría perder información cuando nuestra salida se haga efectiva.

Es posible que tengamos acceso a sistemas específicos única y exclusivamente por pertenecer a ciertos equipos, y es importante asegurarnos que este acceso será revocado, ya que puede ser un vector de ataque desde nuestra cuenta del que nos olvidemos en el futuro.

De manera adicional, es importante dejar listas de distribución o canales de slack que dejen de ser relevantes para nosotros, ya que solamente agregaremos ruido y podemos dar la falsa impresión de que seguimos en el equipo.

Seamos accesibles

Incluso tras meses de trasferencia de conocimientos habrá cosas que no seamos capaz de transmitir, o problemas que surjan cuando no estemos, y para ello es importante mantenernos accesibles.

Si seguimos dentro de la empresa, habrá ocasiones en que alguien de nuestro antiguo equipo contactará con nosotros con dudas o preguntas, especialmente en las primeras semanas o meses, y es una oportunidad para seguir ayudando y apoyando al equipo.

Conclusiones

Los cambios siempre traen incertidumbre, cuando nosotros propiciamos un cambio generamos incertidumbre en la gente a nuestro alrededor pero también en nosotros mismos.

Construir un plan de transición nos ayudará a reducir y limitar esta incertidumbre tanto para nosotros como para nuestros compañeros.

Dejando claras las expectativas, los frentes abiertos, habiendo transferido el conocimiento, documentando proyectos, dejando los recursos con sus nuevos dueños y manteniéndonos accesibles, podremos enfrentar los nuevos retos que nos esperan tras cerrar una etapa profesional, hagámoslo bien.

El patrón BFF: Backend for Frontend

Esta semana he estado leyendo sobre Backend For Frontend o BFF, un patrón de arquitectura de microservicios dio a conocer al mundo uno de los ingenieros de SoundCloud en el año 2015, aunque sigue aplicándose a día de hoy.
En este artículo veremos el problema que tenemos en un entorno de microservicios y cómo el patrón BFF es una de las soluciones posibles.

El problema

Una aplicación web presenta diferentes niveles de complejidad en función de la necesidad. El ejemplo más básico es el de un único sistema que genera el contenido de lado del servidor, donde tanto el frontend como el backend forman parte del mismo, lo que se considera un monolito (por ejemplo, este blog).

Este sistema se puede hacer más complicado si convertimos este monolito en un conjunto de APIs REST unido a un código más complicado del lado del cliente (una Single Page Application o SPA) (o un estéreo-liso). Hasta este momento tenemos una única conexión entre cliente y servidor.

La complejidad puede aumentar mucho más por dos lugares diferentes. Por una parte, podemos necesitar soportar otros canales, como una aplicación móvil, una skill de Alexa, o una API para terceros. Por otra, el backend se puede dividir en diferentes servicios, cada uno con una API específica, a la que nos tendremos que adaptar.

Llegados a este punto, nos encontramos en una posición en el que los clientes (la aplicación móvil, la aplicación principal, la skill de alexa y un desarrollador independiente) dependen de una API común o de una serie de APIs independientes, con lo cual se genera un acoplamiento fuerte entre dos o más sistemas.

El resultado es que todos los clientes acceden a APIs que dan mucha más información de la que ellos necesitan, y no tienen una manera específica de controlar qué información utilizar y cómo leerla. Además, esto limita la capacidad de los desarrolladores de la API, ya que de repente cualquier cambio tiene que ser consensuado con todos los clientes, aumentando la probabilidad de generar regresiones.

La filosofía

La filosofía propuesta por BFF intenta simplificar la relación entre un cliente y su servidor, generando un punto de entrada para cada cliente. Esto significa que un cliente accede a una API exclusiva que soporta casos de uso específicos de cada cliente y que el cliente tiene total libertad para adecuar su API a sus propias necesidades.

En un patrón de microservicios, esto significa generar un servicio intermedio que de soporte única y exclusivamente a este tipo de cliente. Este servicio, desde el punto de vista de pertenencia, le pertenece al equipo del cliente. Volviendo a nuestro ejemplo, el equipo de la Skill de Alexa tendrá una API diferente al equipo de la app móvil, que el de la aplicación de escritorio.

La ventaja principal, que he comentado anteriormente, es que se simplifica el acoplamiento entre una aplicación y sus servicios de backend. Además, es más fácil que un servicio de backend evolucione, ya que las adaptaciones no necesitarán desplegar nuevos cambios en cliente, sino solamente en los adaptadores.

El ejemplo

Supongamos que queremos diseñar la siguiente iteración de Pokemon Go, tenemos una aplicación móvil en la que capturaremos los Pokemon, una web donde podemos ver detalles de nuestras capturas y una Skill de Alexa en la cual podemos saber en qué posición estamos del ranking.

En un desarrollo tradicional con un único endpoint, tendríamos una API REST en el cual todos los clientes efectuarán las llamadas necesarias, todos los clientes utilizarán el mismo protocolo y tipo de datos.

Este sistema convierte nuestra API rest en un punto de fallo para tres sistemas diferentes, con el añadido de que, salvo el sistema web, un fallo que rompa los clientes de iOS y los de la Skill de Alexa pueden tardar en corregirse, ya que depende de las reglas de cada marketplace.

Por otro lado, ¿que pasaría si cada cliente tuviera un servicio asociado? Esto provocaría un aumento considerable en la libertad de los diferentes equipos en desarrollar soluciones en el servidor para sus problemas específicos.

  • El equipo que gestiona la web podría decidir en utilizar GraphQL para generar solamente una petición, y utilizar código isomórfico para tener JavaScript en cliente y servidor.
  • El equipo que gestiona la Skill de Alexa podría utilizar una opción servirles en Lambda utilizando Python.
  • El equipo que utiliza la aplicación iOS, con el objetivo de ahorrar ancho de banda a los clientes, puede decidir montar un servicio a bajo nivel con un socket TCP.

Este cambio no afecta a la lógica de negocio, ya que estos servidores seguirán llamando a nuestro servicio común, que a su vez puede evolucionar de manera separada dividiéndolo en otros tantos servicios.

Esto permite además una evolución mucho más rápida de los sistemas, ya que en caso de un cambio importante, solamente será necesario actualizar los servicios intermedios, y las aplicaciones cliente pueden continuar funcionando.

Los inconvenientes

El principal problema de utilizar una API para cada tipo de cliente es la complejidad, de repente tener un servicio y tres aplicaciones cliente se convierte en tener tres servicios, tres aplicaciones cliente y un cuarto servicio por encima de todas ellas. Esta complejidad no está exenta de coste, lo cual puede repercutir en más hardware, más configuración y encarece el desarrollo.

Otro problema de esta aproximación es que requiere que los equipos de cliente tengan capacidad para generar su propio servicio de backend, lo que implica tener conocimientos de backend en un equipo frontend o un equipo mobile, lo cual no siempre es el caso.

El dilema de dogfooding

Dogfooding (o “eat your own dog food”) es el término que en ocasiones empleamos para utilizar las mismas herramientas y APIs a las que pueden acceder nuestros clientes externos, idealmente con el objetivo de no crear ciudadanos de primera y de segunda.

Sin embargo el peligro que tiene una API pública es el riesgo de romper a clientes externos con cambios que tengan sentido de manera interna pero no de manera externa, así que podríamos considerar la API pública como una API más, con una evolución diferente.

La decisión, to bff or not to bff?

La respuesta es, como siempre, depende. Si tenemos un sistema en el cual un tipo de cliente solamente va a utilizar un subconjunto de la API principal, puede ser buena idea crear un acceso solamente para este tipo de clientes. Ejemplo: Una skill de Alexa para agregar elementos a una lista de tareas, y un portal web para administrarlas.

Si, por el contrario, todos nuestros clientes van a utilizar los mismos métodos de nuestra API, y todos los clientes van a ser exactamente iguales en cuanto a funcionalidad, es posible que no necesitemos más que una API para gobernarlas a todas.

Echando la vista atrás recuerdo un ejemplo muy claro en el que tener un servicio genérico con el objetivo de soportar varios tipos de cliente condicionó muchísimo el diseño del primer y único cliente que se terminó desarrollando, agregando un montón de lógica al cliente de manera innecesaria para adaptar los datos que venían del servicio. Esto se podría haber evitado considerando que el servicio iba a tener un único cliente.

Resumen

BFF es un patrón de diseño de microservicios que nos permite considerar que el acceso a nuestro servicio se realiza desde clientes específicos con necesidades específicas, permite evolucionar las diferentes capas de la aplicación de manera paralela y elimina acoplamiento entre clientes diferentes con casos de uso específicos, siempre y cuando podamos asumir los costes de tener diferentes puntos de entrada a nuestra aplicación mantenidos por diferentes equipos.

Si quieres leer más del tema te recomiendo que eches un vistazo a los siguientes artículos:

Libro – Atomic Habits de James Clear

Hoy, querido lector, te traigo una (no tan breve) reseña de uno de los libros más interesantes que he leído últimamente sobre productividad, concretamente sobre los hábitos, desde un punto de vista fundamentalmente práctico.

El área de productividad lleva llamándome la atención desde hace muchos años, tanto que he leído e intentado poner en práctica muchos métodos, con mayor o menor éxito, y he documentado algunos de estos progresos en artículos como Tareas, Kanban, GTD y otras hierbas

Cuando hace unos meses me recomendaron Atomic Habits, no sabía que esperar. Me había leído (o más bien escuchado) The Power of Habit de Charles Duhigg un libro increíblemente detallado en los aspectos teóricos y físicos de la formación de hábitos aunque un tanto denso, así que otro libro sobre el tema me ponía el listón bastante bajo en cuanto a expectativas, aunque he de decir que las ha superado, con creces.

Nota importante: Este artículo cubre la edición en inglés de “Atomic Habits”, con lo cual la traducción de los conceptos explicados en el libro son propias y pueden no coincidir con la edición en castellano del mismo.

Las cuatro fases de un hábito

Tras una introducción en la que el autor habla de la relación entre identidad, resultados y hábitos, el autor descompone un hábito, en cuatro fases:

  • Cue (desencadenante): Lo que dispara la ejecución de una acción
  • Craving (antojo): Lo que genera la necesidad de llevar a cabo la acción
  • Response (respuesta): La ejecución de la acción.
  • Reward (recompensa): El resultado de la acción.

Las cuatro leyes de los hábitos

Una vez definidas las diferentes fases que componen un hábito y cómo se repiten constantemente a lo largo del día, el libro define el modelo que nos permitirá por una parte, incorporar hábitos positivos a nuestro día a día, así como interrumpir otros que resulten negativos, mediante el uso de cuatro “leyes” que define de la siguiente manera.

Si queremos agregar un hábito positivo a nuestra vida tenemos que conseguir:

  • Que sea obvio (desencadenante)
  • Que sea atractivo (antojo)
  • Que sea sencillo (respuesta)
  • Que sea satisfactorio (recompensa)

En el caso de querer eliminar un hábito que consideremos negativo, el proceso es el mismo, negando las premisas:

  • Que sea invisible (desencadenante)
  • Que sea poco o nada atractivo (antojo)
  • Que sea difícil (respuesta)
  • Que sea poco o nada satisfactorio (recompensa)

El resto del libro se dedica a expandir las definiciones de las cuatro leyes y qué pasos concretos podemos utilizar para implementarlas. Los capítulos son relativamente cortos, específicos y cuentan con un sumario al final de cada uno, así como un progreso en las diferentes leyes.

Ideas interesantes

El libro está lleno de ideas de implementación prácticas relacionadas con los hábitos, de las cuales me gustatría destacar algunas ideas que he sacado de pasajes que he subrayado:

  • Encadenar hábitos, crear un hábito justo a continuación de algo que ya hagamos de manera frecuente, o bien enlazar una acción que queremos hacer con una acción que necesitamos hacer.
  • Con el fin de crear un hábito, tener un entorno estable en el que todo tiene un lugar y un propósito hace que el entorno en sí mismo se convierta en el desencadenante del hábito y que posteriormente la acción a llevar a cabo sea más sencilla.
  • Podemos romper hábitos, pero es prácticamente imposible olvidarlos del todo, resistir la tentación es una estrategia poco eficiente, siendo más útil reducir la exposición al desencadenante (que sea invisible).
  • Es importante destacar la diferencia entre movimiento y acción, movimiento es pensar en escribir este artículo durante 3 meses, fijar un calendario, poner una lista de las ideas que discutir y tener la sensación de “progreso”, acción es sentarse durante 2 horas a escribir. Solamente la acción produce resultados.
  • La ley de los dos minutos nos permite fijar un objetivo ridículamente sencillo, con la idea de que empezar a trabajar en la acción sea fácil, y que no cueste trabajo seguir adelante.
  • Para llevar la cuenta de nuestros hábitos, podemos utilizar un registro, en el cual vamos anotando de manera inmediata y diaria los hábitos que cumplimos. Esto nos permite ser sinceros con nosotros mismos, ya que, habitualmente, pensamos que hacemos las cosas (bastante) mejor que como las hacemos realmente. Es importante que nuestra lista sea corta y concisa, ya que corremos el riesgo de querer llevar la cuenta de muchas cosas, y eso rompa el sistema.
  • Busca tu zona óptima de dificultad. Si algo es demasiado sencillo te resultará aburrido, si es demasiado complicado, te resultará inalcanzable, encuentra el punto medio para poder seguir progresando de tal manera que no te quedes atascado, ya que la mayor amenaza no es fallar, sino aburrirse.

Apéndices y extras

En la parte final del libro, podremos ver un resumen de todas las sugerencias, simplificadas para un acceso rápido, así como enlaces a los trackers, a listas paso a paso para mantener hábitos, y extras sobre hábitos en la paternidad o en el mundo de los negocios.

Conclusiones

Atomic Habits es un libro que no puedo dejar de recomendar si te gusta la ciencia, y sobre todo la práctica de la mejora continua a través de pequeños cambios que representan grandes resultados a lo largo del tiempo. El estilo práctico, unido a los ejemplos y detalles como resúmenes al final de cada capítulo y extras al principio y al final, hacen que la lectura sea rápida y entretenida.

Te recomiendo, además, que visites el blog del autor James Clear en el que define de manera monográfica muchos de los temas tratados en el libro, con lo cual si te gusta el libro te gustará el blog, y viceversa.

Atomic Habits está disponible en formato físico y ebook en Amazon y como audiolibro en Audible

Espero que te resulte tan interesante como a mí.

Creando un sistema basado en reglas en Java

Hace tiempo cuando intentaba aprender Scala, compré el libro Exercises for Programmers y últimamente he dedicado algo de tiempo a hacer uno de los ejercicios, que consiste en un sistema basado en reglas muy simple.

Los sistemas basados en reglas son un subconjunto de los sistemas expertos, que a su vez se engloban en el área de la inteligencia artificial. En el contexto de este artículo, simplificaremos los conceptos, quedándonos con un simple árbol de decisión, que nos permitirá llegar a una conclusión basándonos en las respuestas del usuario.

Podemos encontrar ejemplos de estos sistemas en asistentes paso-a-paso, en las centralitas telefónicas (“Para consultar el saldo, pulse 1”) y en los asistentes digitales como Alexa, Siri, Cortana o Google. Cuando escribimos un sistema de este tipo, nuestra lógica tendrá el siguiente aspecto:

sistema pregunta a.
si usuario responde si:
    sistema pregunta b.
    si usuario responde = "si"
        sistema responde c <- Respuesta final
si no
    sistema pregunta d
...

Esta información puede vivir en nuestro código fuente en formato de if, else anidados o puede formar parte de los metadatos de nuestro sistema. En el artículo de hoy diseñaremos uno de estos sistemas utilizando Java y la siempre útil consola de comandos.

Además de escribir nuestro sistema de reglas, en este artículo mencionaremos por encima los siguientes temas:

  • Tests unitarios con JUnit y AssertJ
  • Generación de Setters y builders con Lombok
  • Manejo de Ficheros yaml con snakeyaml
  • Inyección de dependencias e inversión de control
  • Creación de paquetes JAR con Gradle incluyendo dependencias

Los tests

Una práctica que intento seguir a la hora de hacer este tipo de ejercicios, es empezar por las pruebas al más puro estilo TDD, definiendo de manera inicial el estado al que queremos llegar.

Podemos emplear JUnit y AssertJ para ejecutar los tests, y agregar las referencias a Gradle es tan sencillo como utilizar las siguientes líneas:

testCompile group: ‘junit’, name: ‘junit’, version: ‘4.12’
testCompile(“org.assertj:assertj-core:3.11.1”)

Con ello podemos escribir nuestro primer test:

@Test
public void question() {
    assertThat(expertSystem.getMessage()).isEqualTo("Are you able to see any wifi network?");
}

Una vez pedido el estado inicial, hemos de comprobar que el motor pasa del primer estado al siguiente, como hemos en el siguiente test:

@Test
public void firstAnswerYes() {
    assertThat(expertSystem.getMessage()).isEqualTo("Are you able to see any wifi network?");
    expertSystem.answer(true);
    assertThat(expertSystem.getMessage()).isEqualTo("Is the network ID visible?");
    assertThat(expertSystem.isDone()).isFalse();

Finalmente, podemos comprobar que hemos llegado a un estado final con el siguiente test:

@Test
public void secondAnswerYesYesDone() {
    assertThat(expertSystem.getMessage()).isEqualTo(Are you able to see any wifi network?");
    expertSystem.answer(true);
    assertThat(expertSystem.getMessage()).isEqualTo("Is the network ID visible?");
    expertSystem.answer(true);
    assertThat(expertSystem.getMessage()).isEqualTo("Contact your network provider");
    assertThat(expertSystem.isDone()).isTrue();
}

Con estos tests, definimos un sistema de reglas que:

  • Recibe una respuesta binaria, que puede ser sí o no.
  • Proporciona el estado actual así como la información de si el estado actual es final o no.

El motor de reglas

Para este ejemplo, el motor de reglas no es más que un árbol binario con una clase muy sencilla que contiene dos hijos, “sí” y “no”, para cada rama:

@Getter
@Builder
class Stage {

    private String status;
    private Stage yes;
    private Stage no;

    boolean isEnd(){
        return yes == null && no == null;
    }
}

Para crear tanto la construcción de los objetos como los getters como el patrón builder podemos recurrir a Lombok, del que ya hemos hablado en otros artículos de este blog, y que podemos agregar a nuestro modelo de Gradle con las siguientes líneas:

compileOnly ‘org.projectlombok:lombok:1.18.8’
annotationProcessor ‘org.projectlombok:lombok:1.18.8’

A la hora de pasar de una etapa a otra, el motor de reglas simplemente decide qué hijo tiene que buscar:

private Stage current; //Aquí inicializaremos la etapa inicial

public String getMessage() {
    return current.getStatus();
}

public void answer(boolean answer) {
    if (answer){
        current = current.getYes();
    } else {
        current = current.getNo();
    }
}

public boolean isDone() {
    return current.isEnd();
}

Una comprobación que no forma parte del código es qué pasa si la etapa ya es final. Por otra parte se podría transformar bloque IF en un operador ternario, dando como resultado algo como current = answer ?? current.getYes() : current.getNo();

El almacenamiento

En el apartado anterior no hemos mencionado cómo inicializar las reglas. Podemos empezar por crear los objetos como parte del constructor, dando como resultado algo así:

Stage root = Stage.builder()
        .status(“Are you able to see any wifi network?”)
        .yes(Stage.builder()
                .status(“Is the network ID visible?”)
                .yes(Stage.builder()
                        .status(“Contact your network provider”)
                        .build())
                .build())
        .no(Stage.builder()
                .status(“Wireless network might be off. Reboot computer”)
                .build())
        .build();

Sin embargo, esta aproximación genera un fuerte acoplamiento entre el código y los datos, así que es una buena práctica sacar las reglas del código a un formato diferente, optando en este caso por YAML.

Al transformar nuestro árbol de Java a YAML tenemos como resultado la siguiente estructura, y este fichero lo podemos almacenar en la carpeta resources de nuestro proyecto:

text: "Are you able to see any wifi network?"
yes:
  text: "Is the network ID visible?"
  yes:
    text: "Contact your network provider"
  no:
    text: "Reboot the wireless router"
no:
  text: "Wireless network might be off. Reboot computer"

Para leer el fichero YAML podemos utilizar SnakeYaml, que podemos importar en Gradle con la siguiente línea:

compile group: ‘org.yaml’, name: ‘snakeyaml’, version: ‘1.8’

Finalmente, podemos cargar el fichero YAML en memoria de la siguiente manera.

class YamlLoader implements FileLoader<Stage> {

    public Stage loadFromFile() {
        Yaml yaml = new Yaml();
        InputStream inputStream = this.getClass()
                .getClassLoader()
                .getResourceAsStream("options.yaml");
        Map<Object, Object> obj = (Map<Object, Object>) yaml.load(inputStream);

        return getStage(obj);
    }

    private Stage getStage(Map<Object, Object> obj) {
        if (obj == null) {
            return null;
        }

        return Stage.builder()
                .status(obj.get("text").toString())
                .yes(getStage((Map<Object, Object>) obj.get(true)))
                .no(getStage((Map<Object, Object>) obj.get(false)))
                .build();
    }
}

Para no tener un acoplamiento entre nuestra clase ExpertSystemy YamlLoader, la segunda implementa una interfaz genérica llamada FileLoader que simplemente define un método, lo que nos da la posibilidad de agregar otros gestores de ficheros en el futuro como xml o JSON.

public interface FileLoader <T> {
    T loadFromFile();
}

Finalmente la conexión entre nuestra clase ExpertSystemy el gestor de ficheros se realiza en el constructor:

public ExpertSystem(FileLoader<Stage> fileLoader) {
    current = fileLoader.loadFromFile();
}

Este último paso no deja de ser inyección de dependencias e inversión de control. De esta manera, nuestra clase ExpertSystem es completamente independiente del formato en el que almacenemos nuestros datos, y podemos probarla de manera aislada, mientras mantenemos nuestra lógica de carga de YAML independiente de la lógica del motor de reglas.

La interacción

Para que nuestro código sea utilizable necesitamos, además de un algoritmo que funcione, una manera de interactuar con nuestro sistema. Para eso, volvemos a los mecanismos basados en System.outy System.in para escribir y leer por la consola.

public class UI {

    public static void main(String[] args) {
        ExpertSystem system = new ExpertSystem(new YamlLoader());

        Scanner scanner = new Scanner(in);

        while (!system.isDone()) {
            out.printf("%s %s ", system.getMessage(), Option.getOptions());
            system.answer(Option.parse(scanner.nextLine()));
        }

        scanner.close();
        out.println(system.getMessage());
    }
}

Finalmente, podemos extraer la gestión de la entrada a una clase específica llamada Option:

class Option {

    private static final String truthy = "yes";
    private static final String falsy = "no";

    static String getOptions() {
        return String.format("[%s/%s]:", truthy, falsy);
    }

    static boolean parse(String input) {
        if (input.toLowerCase().equals(truthy)) {
            return true;
        } else if (input.toLowerCase().equals(falsy)) {
            return false;
        }

        throw new IllegalArgumentException();
    }
}

Esta clase analizará la entrada que recibimos de la consola y generará un valor verdadero o falso, que luego podemos pasar posteriormente a nuestro motor.

Creando nuestro paquete jar

El último paso de nuestro sistema es empaquetarlo para pode distribuirlo. En nuestro caso lo que queremos es un fichero jar que se pueda ejecutar de manera independiente.

Para ello, hemos de modificar nuestro fichero Gradle para agregar ciertas directivas que, por una parte, establecen la clase principal de nuestro proyecto, y por otra parte, fuerzan a que se combinen las diferentes dependencias que tiene nuestra aplicación. Este paso se muestra a continuación:

jar {
    manifest {
        attributes "Main-Class": "net.rlbisbe.expert.UI"
    }

    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

Finalmente, solamente tenemos que ejecutar java -jar NuestroPaquete.jar, y podremos ver nuestro sistema en funcionamiento:

Recapitulando

A lo largo de este artículo, hemos visto cómo crear un simple sistema basado en reglas, cómo cargar dichas reglas de un fichero YAML, mientras repasábamos conceptos como inversión de control e inyección de dependencias, pruebas unitarias, y aprendíamos a manejar dependencias en Gradle.

No pierdas la oportunidad de echarle un ojo a Exercises for Programmers que tiene este y otros problemas para aprender nuevos lenguajes, probar maneras diferentes de hacer las cosas o practicar conocimientos.

Puedes encontrar el código fuente de este ejemplo en Github – Expert-System

Sobre cultura de trabajo en equipos remotos

El pasado jueves estuve en la primera sesión del grupo MADnagers in tech y el tema de conversación era “Gestionando equipos en remoto”, presentado por Pablo Carranza, Head of Engineering en Cabify.

Aunque tuve la mala suerte de llegar un poco tarde y perderme los primeros minutos de presentación, lo que pude ver me resultó realmente interesante, así que no quería perder la ocasión de compartir mis notas y aprendizajes de esta sesión.

Dos ritmos de trabajo en paralelo

Una de las ideas más interesantes que se comentó fue la de los dos ritmos de trabajo a los que nos enfrentamos en una organización, y que funcionan a la par:

El primero se refiere al aquí y ahora, las características, bugs, y problemas a los que se está enfrentando el equipo a fecha de hoy. Este ritmo es el que se suele ver diariamente en las standups, en los correos “urgentes” y en la cola de tickets que nos vienen de atención al cliente. El responsable de llevar este ritmo es el propio equipo, que tiene todo el contexto sobre el aquí y ahora, siendo la labor del líder del equipo la de desbloquear situaciones, pero no la de controlar cada tarea.

El segundo se refiere al futuro, aquí es donde están las nuevas peticiones, el estado de los proyectos que se ejecutarán más adelante, las nuevas iniciativas, las migraciones, y todo lo que no tenga el carácter de “Urgente”. En este caso el responsable de llevar este ritmo es el líder del equipo, de fijar el contexto y mantener la conversación sobre el futuro, especialmente a la hora de encontrar bloqueos que afecten al equipo

Uno de los puntos en los que más incidió Pablo fue en la necesidad de que tanto el presente como el futuro convivieran, idealmente, en el sistema de control de tareas, ya sea Jira, Github, Gitlab u otros.

Esto es debido a que el sistema de control de tareas permite mantener una conversación de manera asíncrona y pública y en diferentes hilos. Esto es especialmente importante en el caso de equipos remotos, ya que por una parte, no todos los miembros del equipo tienen que estar disponibles a la misma hora, y por otra, garantiza que en el futuro cualquier miembro del equipo pueda acceder a este historial, especialmente nuevos miembros.

Roadmaps permanentes y controlados

Otro punto destacado fue la gestión del Roadmap, en la que pasamos de un documento de Word compartido por email a un sistema controlado y versionado.

Un roadmap es importante en cuanto a que define la dirección a la que vamos, y por tanto es un documento que debería cambiar lentamente y de manera controlada. Si un roadmap está cambiando constantemente se convierte en un documento que no es fiable, y por tanto se le deja de presentar atención.

Es por ello que una idea que plantea Pablo es la de mantener el roadmap de manera controlada y controlar qué cambios se hacen al mismo. La manera más fácil de hacer esto es mantener el roadmap en un fichero markdown dentro de nuestro control de versiones, que las actualizaciones del mismo se realicen vía pull request, y que se pueda tener una conversación sobre la misma de manera pública y asíncrona.

Esto además nos permitiría mantener el histórico de donde estábamos hace 1 año, 6 meses, y ver si lo que queríamos hacer entonces lo queremos hacer ahora.

Si no está escrito, no pasó

En este punto, mencionaba dos ceremonias que suelen ocurrir cuando tenemos un equipo parcialmente remoto, las reuniones formales, como por ejemplo la standup, y las reuniones informales, como las conversaciones de pasillo.

Por lo general, salvo que exista una agenda definida y unos minutos tras la reunión, no se suele dejar constancia de que lo que se ha contado en una reunión, especialmente si la información solamente se comparte en ese momento y no queda en una tarea.

Esto es especialmente delicado en lo que respecta a las conversaciones de pasillo, ya que suelen ir acompañadas por una toma de decisiones que no suele quedar documentada, ni la decisión, ni las razones de la misma.

Pensando en este tema me ha venido a la memoria el año 2012, cuando formaba parte del equipo de Códice Software haciendo PlasticSCM, que durante las standups un miembro del equipo tomaba nota del estado que compartía cada miembro del equipo, y luego ese estado se actualizaba en una wiki de manera pública.

De esta manera, al final del sprint se podría comprobar cómo había sido el progreso del mismo, y habría un registro. Por otra parte, si alguien se incorporaba después de unos días de vacaciones, era tan sencillo como ir al registro del sprint, leer el estado y ponerse al día de manera asíncrona,

Cuidado con los Silos

Otro de los puntos comentados se refería a cómo el no compartir la información de manera pública puede acabar provocando silos de información, cuando solamente ciertas personas conocen el estado de un sistema porque lo diseñaron ellos, o peculiaridades de una integración que no están documentadas porque es conocimiento tribal.

Para ello es muy importante tomarle el pulso al equipo, ver cómo están hablando, cómo se están comunicando y encontrar los mejores canales para ello, wikis, sistemas de tareas, repositorios y otros mecanismos más allá de reuniones, chats o email.

Equipos remotos y la ley de Conway

La importancia de mantener unos niveles de comunicación adecuados en un equipo va más allá de que sea más sencillo seguir el rastro a tareas, de que el equipo no tenga que estar preocupado de lo que va a decir en la standup, o que las reuniones de sincronización se extiendan durante varias horas hasta que el estado quede claro.

De acuerdo con la ley de Conway, esto afecta directamente a cómo producimos el software en nuestras organizaciones:

Las organizaciones dedicadas al diseño de sistemas […] están abocadas a producir diseños que son copias de las estructuras de comunicación de dichas organizaciones

Melvin Conway

Por tanto, si formamos parte de equipos remotos y nuestra estructura de comunicación es pública y asíncrona acabaremos haciendo mejor software.

Conclusiones

  • Tenemos muchos sistemas a nuestra disposición para compartir el estado de nuestras tareas más allá de una reunión de 15 minutos en la que cada miembro del equipo tiene 30 segundos para hablar.
  • Compartir información de tal manera que no necesitemos a todos los miembros del equipo presentes a la misma vez nos permite ganar tiempo, ya que la información se mantiene en un punto común y de manera estable, especialmente a la hora de tomar decisiones, documentando las decisiones tomadas y las razones para las mismas.
  • Mantener la información públicamente accesible y simplificar canales de comunicación sencillos permite mitigar la aparición de silos de información, en las que solamente un subconjunto del equipo conozcan cierta información que todos deberían conocer.
  • Conseguir un sistema que permita que el estado esté fácilmente disponible, y que los canales de comunicación estén claros, no solamente afecta a la actitud del equipo, sino que tiene un impacto directo sobre el software que producimos.

Lecturas adicionales

Pablo escribió un artículo en su blog en el que habla de estos temas y los desarrolla con un gran nivel de detalle, además durante el evento mencionó el Remote Manifesto de Gitlab, y la teoría de las limitaciones (theory of constraints) que se hizo conocida en el ibro The Goal y que se ha llevado al campo de IT en el libro The Phoenix Project, que comenté el pasado año.