Píldora. Migrando de Travis a GitHub Actions

Hace un tiempo que me está volviendo a llamar la atención grabar de vez en cuando algún screencast sobre temas de desarrollo de software, a modo de píldoras sobre algún tema muy concreto. En el pasado ya experimenté un poco con ello pero lo dejé olvidado.

En esta ocasión, aprovechando que he migrado hace poco un pipeline de integración continua de un proyecto personal de Travis a GitHub Actions aproveché para hacer un repaso y grabarlo.

Aquí os dejo el vídeo:

También hay una pull request donde pueden verse esos cambios con más detenimiento.

Charlando sobre slicing vertical

Hace unos días Agile Delivery organizó un evento en formato Lean Coffee en el que estuvimos unas cuantas personas hablando sobre slicing de producto de software. Lo del slicing viene a ser simplemente ir haciendo rebanadas para organizar el trabajo desde problemas grandes a más pequeños para hacerlos más manejables.

Con la visión waterfalera lo habitual era hacer slicing con un enfoque horizontal descomponiendo por tipo de actividad, por ejemplo: hacer el análisis, diseñar la UI cubriendo todas las casuísticas reflejadas en el análisis, diseñar la base de datos, implementar el frontend de la UI diseñada, programar la lógica de negocio de lo que se definió, hacer pruebas para que todo funciona tal y como se indicó en el análisis… y acabar desplegando a modo big bang como último paso. Así que pueden pasar muchos meses o años para empezar a obtener feedback de que lo que hemos construido aporta valor o para empezar a recuperar la inversión.

La visión agilista cambia esta perspectiva a algo que parece mucho más razonable para crear mejores productos de software, construirlos de forma iterativa e incremental. Para ilustrarlo, y aunque suene un poco vetusto, estos son los principios del manifiesto ágil que se refieren a ello:

  • Welcome changing requirements, even late in development. Agile processes harness change for the customer’s competitive advantage.
  • Deliver working software frequently, from a couple of weeks to a couple of months, with a preference to the shorter timescale.
  • Working software is the primary measure of progress.
  • Simplicity–the art of maximizing the amount of work not done–is essential.

Atención especial a ese art of maximizing the amount of work not done, principalmente desde el punto de vista de producto. Debemos intentar limitar al máximo el alcance de lo que construimos para que, con el mínimo esfuerzo posible, tratemos de conseguir el máximo valor o aprender lo máximo posible. Esto vendría a conseguirse a través de hacer slicing vertical para organizar el trabajo.

A día de hoy, aún con el boom de lo ágil que hay en el sector tech, es bastante habitual seguir viendo muchos vicios heredados del waterfall. Esto es porque aunque la teoría es simple, llevarlo a la práctica no lo suele ser. Requiere que las organizaciones y equipos tengan una mentalidad enfocada a producto y cierta capacidad técnica para llevarlo a cabo.

  • La mentalidad de producto ayuda a plantear esos slices. Esto es hacer que se focalicen mucho más en los objetivos a conseguir o en las hipótesis que se pretendan validar y que busquen el camino más corto para hacerlo, evitando convertir a los equipos en meros feature factories de quienes se esperan que implementen a pies juntillas lo que alguien les dice.

  • Ciertas capacidades técnicas son necesarias para que los equipos no se vean limitados o paralizados por esas cuestiones, además de para trabajar con más tranquilidad. Por ejemplo tener arquitecturas y diseños que absorban bien los cambios, confiar en los tests automáticos para comprobar que tanto lo nuevo como lo anterior sigue funcionando correctamente, poder entregar software de forma frecuente y con confianza…

En fin, os dejo el video de la conversación que tuvimos, a mi me resultó una sesión muy interesante y entretenida. Cerca del minuto 19 de la grabación es cuando se empieza a entrar en harina:

Salieron experiencias, consejos y referencias más que interesantes. Además pudimos enfrentar puntos de vista diferentes que creo que también aporta, ya que en eso del vertical slicing creo cada uno vamos explorando cosas diferentes que dependiendo de los contextos pueden funcionarnos mejor o peor.

La naturaleza del desarrollo de software, cerrando el círculo del valor

Estoy releyendo el libro de The Nature of Software Development. Keep It Simple, Make It Valuable, Build It Piece by Piece de Ron Jeffries, uno de esos libros donde te encuentras condensadas ideas que merecen ser revisadas de vez en cuando.

Este libro habla de la forma natural de hacer software, the natural way lo llama Jeffries, que viene a ser algo tan simple como focalizarse en entregar valor pronto y a menudo. Simple, que no fácil.

La naturaleza del desarrollo de software

Este no es un libro de recetas con trucos para hacerlo, si no de ideas sobre las que reflexionar y que anima a que explores por tu cuenta modos de hacerlo. Como muestra este es el capítulo resumen de la primera parte del libro llamada The circle of value.

Cerrando el círculo del valor:

  • Valor es lo que queremos. Las funcionalidades entregan valor. Entregar funcionalidades pronto nos da valor antes.
  • Gestionar observando el valor funciona mejor que gestionando fechas o artefactos que no entregan valor.
  • Planificar funcionalidad es bastante fácil de hacer. Estima si debes hacerlo. Seleccionar el trabajo basado en el “tiempo que hizo ayer” funciona mejor.
  • Construir por funcionalidades nos requiere construir un producto pequeño y completo cada pocas semanas. Ese producto debe funcionar siempre correctamente y debería estar siempre bien diseñado.
  • Desarrollo debe entregar funcionalidades reales que funcionen. El producto debe estar bien testeado. La personas de negocio y las de desarrollo contribuyen al testing. El producto debe estar bien diseñado. Los desarrolladores mantienen el diseño vivo todo el tiempo.

Esto es todo al respecto. Muy simple. Un compromiso desde la cima del negocio, hasta los managers y desarrolladores individuales, es todo lo necesario. ¡Vamos! ¡Enséñame el software!

Diseño incremental de software a partir de las interacciones (parte 2)

Esta es la continuación de Diseño incremental de software a partir de las interacciones (parte 1). En la primera parte traté temas más relacionados con el descubrimiento de producto, formalización de backlog y refinamiento de historias de usuario.

En esta parte veremos como aterrizarlo en código, practicando Specification by Example y ATDD para identificar las interacciones y TDD para ayudarnos con el diseño incremental de las interacciones.

Dibujo representando la autosimilaridad de Lean Startup, ATDD y TDD

¿Qué vamos a hacer?

El hilo conductor va a ser un producto orientado a aficionados al boxeo. Aunque falte aterrizar la mayor parte del producto, sabemos que la visión es que cualquier aficionado al noble arte pueda puntuar combates, ya sean vistos en diferido como durante veladas en directo.

Habitualmente en los combates de boxeo profesional, además del árbitro que está sobre el ring, hay tres jueces que llevan el conteo de puntuaciones de cada asalto en sus tarjetas, para decidir quien vence el combate en caso de no terminar en nocaut. No es extraño que de vez en cuando surjan polémicas con los resultados de los jueces como se puede ver en redes sociales y foros especializados, donde muchos aficionados del comparten sus puntuaciones.

Así que el MVP que queremos lanzar debe cubrir que esos aficionados puedan compartir y comparar puntuaciones de cualquier combate de boxeo de una forma más ordenada. Tras eso trataremos de buscar algo de tracción para ganar usuarios y comprobar que el producto tiene sentido, a partir de ahí iremos iterando y seguir evolucionando enfocándonos también en distintas posibilidades de servicios premium.

Además de la propia puntuación de los combates, una funcionalidad que parece bastante evidente es registrar los combates que se van a puntuar indicando la fecha, los boxeadores…

Especificaciones en formato Gherkin

Como comenté en la primera parte, me parece interesante practicar Specification by Example con Cucumber y Gherkin por facilitar la colaboración con personas no técnicas y el evitar usar conceptos técnicos en las especificaciones. Esto nos puede ayudar a identificar mejor los nombres para los diferentes casos de uso o interacciones.

Por ejemplo, para este caso, aún cuando no hubiéramos decidido cómo vamos a exponer el API u otros detalles de implementación; podríamos dejar ya la especificación lista para describir la fucionalidad de registro de combate.

A falta de más escenarios extremos y de errores, esta podría ser una especificación con la que podríamos empezar:


Feature: Register a fight
  Background:
    Given an existing boxer called "Kerman Lejarraga"
    And an existing boxer called "Bradley Skeete"

  Scenario: successfully if all is ok
    Given an event in "Bilbao Arena" at "28/04/2018"
    When I register the fight in the event for "Kerman Lejarraga" and "Bradley Skeete" for 12 rounds
    Then the fight is successfully registered

  Scenario: successfully if all is ok but place is not present
    Given an event at "28/04/2018"
    When I register the fight in the event for "Kerman Lejarraga" and "Bradley Skeete" for 12 rounds
    Then the fight is successfully registered

 Scenario: fails without event date
     Given an event in "Bilbao Arena"
     When I register the fight in the event for "Kerman Lejarraga" and "Bradley Skeete" for 12 rounds
     Then the fight is not registered

Scenario: fails without number of rounds
    Given an event in "Bilbao Arena" at "28/04/2018"
    When I register the fight in the event for "Kerman Lejarraga" and "Bradley Skeete"
    Then the fight is not registered

Nótese de lo verboso de cada step de gherkin. Con el tiempo aprendí a buscar serlo para: no caer en especificaciones falsamente específicas, que resulten más fáciles de entender para personas no técnicas y de paso para evitar conflictos de nombrado entre steps.

Esto es algo que para la mayoría de programadores que empiezan a trabajar con Cucumber resulta antinatural, es habitual tener la tentación de tratar de tener steps muy reutilizables y poder combinarlos. En esto la mayoría de posts introductorios a Cucumber no ayudan nada…

Mi recomendación es que no. Para reutilizar código, hazlo en la implementación de los tests, no en las especificaciones en formato gherkin.

ATDD desde las especificaciones

Ya hemos pasado por la fase de especificación para esta funcionalidad, ahora pasaríamos a la parte de desarrollarla. Mi flujo suele ser preparar un test de aceptación que quede roto para entrar ya en el ciclo de TDD.

Cucumber ofrece un generador de snippets de código a partir de las especificaciones en Gherkin que podemos copiarlos para usarlos a modo de plantilla. En mi flujo de trabajo suelo dejar preparados los Given que sean necesarios y dejo el When o Then una condición que deje el test roto a modo de recordatorio, para volver a ello al acabar la funcionalidad.

Este sería un ejemplo de snippet generado en Java sobre la que vamos a partir:


@Given("an existing boxer called {string}")
public void anExistingBoxerCalled(String string) {
    // Write code here that turns the phrase above into concrete actions
    throw new cucumber.api.PendingException();
}

@Given("an event in {string} at {string}")
public void anEventInAt(String string, String string2) {
    // Write code here that turns the phrase above into concrete actions
    throw new cucumber.api.PendingException();
}

@When("I register the fight in the event for {string} and {string} for {int} rounds")
public void iRegisterTheFightInTheEventForAndForRounds(String string, String string2, Integer int1) {
    // Write code here that turns the phrase above into concrete actions
    throw new cucumber.api.PendingException();
}

@Then("the fight is successfully registered")
public void theFightIsSuccessfullyRegistered() {
    // Write code here that turns the phrase above into concrete actions
    throw new cucumber.api.PendingException();
}

...

Así que partir de aquí dejaríamos los steps de Given listos y el primer escenario que queramos implementar roto.

Posponiendo decisiones

Un par de los puntos más interesantes del estilo de arquitectura Clean/Hexagonal son la cambiabilidad y por ello la testeabilidad. Gracias a la cambiabilidad podemos posponer decisiones que típicamente se asumen al arrancar la funcionalidad: ¿qué tecnología elegimos para la persistencia? ¿y para la mensajería? ¿qué librerías y frameworks usamos?

En este caso vamos a ver un ejemplo de ello, vamos a ir desarrollando poco a poco las funcionalidades a partir de las especificaciones y dejando las decisiones técnicas para el futuro. Por el momento no nos vamos a meter en cosas como en qué base de datos vamos a persistir, qué framework vamos a utilizar o incluso en definir cómo vamos a exponer el API.

Vamos implementar el core de nuestra aplicación como una librería que se pueda utilizar desde cualquier framework. En este caso será en Java en un módulo de Maven, por el momento sólo con dependencias a librerías que facilitan el testing.

Ejecutaremos los tests de Cucumber ejercitando clases que van a representar cada funcionalidad. Esas clases las vamos a llamar Use Cases, van a estar escritas en Java y usaremos algunos conceptos de Domain-Driven Design; como implementaciones del patrón Repository en memoria para tener tests de aceptación muy rápidos.

Veremos que los tests de aceptación con Cucumber no tienen por qué ser lentos ni ir sobre la UI, más sobre eso en el curso que grabamos en CodelyTV.

¿Use Cases?

Nos referimos como Use Case a cada clase que sirve como punto de entrada de una funcionalidad de nuestra aplicación, en terminología DDD serían los Application Services.

En este caso veremos que no saben de la infraestructura con la que van a ser expuestos; podrían estar expuestos vía APIs REST, AMQP, GraphQL, gRPC, JSON-RPC… esa responsabilidad la dejaríamos en una capa más acoplada a frameworks/librerías que llamarían a estos Use Cases.

Aunque no se esté trabajando en proyectos con arquitecturas Clean/Hexagonal es un tipo de abstracción que en mi experiencia resulta útil. Ya sólo por hacer el ejercicio de pensar en nombres que deberían ser significativos para el negocio y facilitar el inventario de las funcionalidades, además de separarlo de artefactos más técnicos (Controller, Resolver, Service…).

Entrando al flujo de TDD (Outside-In)

  • crear el test del use case
  • crear un use case
  • completar fight con entity y repositories

  • Implementar tests automáticos desde especificaciones gherkin con cucumber.
  • Artefactos tácticos de Domain Driven Desing.
  • TDD Outside-In, empezando desde las interacciones o use cases.
  • Arquitectura hexagonal/clean, escapando de frameworks.

Los principios DevOps: The Three Ways

Llevamos ya unos años en los que se habla de DevOps, un término que originalmente se acuñó para referirse a transformar el modo en el que se entrega software en las organizaciones basándose en ideas de Lean Manufacturing.

Pero en los últimos tiempos, al explotar su popularidad, parece haberse asumido por gran parte del sector como la forma de llamar a los equipos o ingenieros de plataforma, o en otros sitios simplemente como la forma moderna de llamar a los administradores de sistemas; ya que se asocia principal, y a veces únicamente, con despliegues automatizados o con montar infraestructura como código.

Personalmente, es algo que de vez en cuando he hablado con algunos compañeros de profesión, y aunque no es algo que me quite el sueño, sí me apena un poco el hecho de que con ello se pierda la parte más importante del mensaje: romper silos organizativos para centrarse en entregar valor.


Los Three Ways, que son mencionados en libros como The Phoenix Project o en The DevOps Handbook, vienen a ser los principios de los que derivan los comportamientos y patrones que nos ayudarán a romper esos silos y a poner foco en la entrega de valor:

  • The First Way: aumentar el flujo de entrega al cliente previniendo defectos.

  • The Second Way: amplificar los ciclos de feedback para prevenir problemas y mejorar la calidad en origen.

  • The Third Way: crear y fomentar una cultura de experimentación y aprendizaje continuos.

Representación de Los Three Ways de Devops

The First Way: aumentar el flujo de entrega

Hasta que algo no se está usando en producción por los usuarios y/o clientes no se está creando valor, así que deberíamos buscar desplegar de forma temprana y frecuente en producción incrementando la fiabilidad y calidad de lo entregado.

Sus principios son:

  • Hacer el trabajo visible. Típicamente con algún tipo de tablero (tipo kanban, sprint backlog…) donde tener el inventario de trabajo a realizar y su flujo entre “work centers”, permitiendo así detectar cuellos de botella entre ellos.
  • Limitar el WIP. Básicamente tener más foco para acabar antes y pasar el trabajo al siguiente “work center”.
  • Reducir el tamaño de de los paquetes de trabajo: Entregar menos funcionalidades pero con mayor frecuencia.
  • Reducir el número de traspasos: Reducir pasos donde se encole el trabajo y que alargue el tiempo de entrega.
  • Identificar y resolver continuamente los “constraints”: Para reducir tiempos de entrega necesitamos estar continuamente identificando el “constraint” del sistema y mejorar su capacidad de trabajo.

    In any value stream, there is always a direction of flow, and there is always one and only one constraint; any improvement not made at that constraint is an illusion. (Eliyahu M. Goldratt)

  • Eliminar las dificultades y desperdicios del flujo de valor: Cualquier cosa que cause retrasos de cara a la entrega al cliente o actividades que puedan evitarse sin afectar al resultado. En Implementing Lean Software Development categorizan los desperdicios en: Trabajo parcialmente hecho, procesos extra, funcionalidades extra, cambios de tareas, esperas, motion o esfuerzo de transporte/comunicación entre “work centers”, defectos, trabajo manual o no estándar y heroicidades.

The Second Way: amplificar los ciclos de feedback

Una vez entregado, necesitamos feedback rápido y constante, la meta es conseguir un sistema de trabajo cada vez más seguro y resiliente, detectando y resolviendo los problemas cuando son más pequeños y más fáciles de resolver. Pero los fallos son inevitables en sistemas complejos, cuando ocurren se deben asumir como oportunidades de aprendizaje frente a buscar o castigar culpables.

Sus principios son:

  • Ver los problemas en cuanto ocurren: Tener telemetría que muestre cómo se comportan los componentes del sistema en producción para detectar cuando no lo hacen como se espera.
  • Resolver los problemas para construir nuevo conocimiento: Una vez que un problema ocurre debemos resolverlo como un enjambre, movilizando a quienes haga falta para arreglarlo. Llegando a parar todo lo necesario del mismo modo que en el mundo de manufactura se utiliza una cuerda andon.
  • Empujar la calidad cerca del origen: Debemos encontrar y arreglar los problemas en cada área de control, las responsabilidades y toma de decisiones deben ocurrir donde se hace el trabajo.
  • Facilitar la optimización de los “work centers” posteriores: Diseñando para facilidad operacional, donde los atributos de calidad (rendimiento, configurabilidad, testabilidad…) tienen la misma importancia que las funcionalidades.

The Third Way: experimentación y aprendizaje continuos

El objetivo es crear un entorno de alta confianza, reforzando la idea de que estamos aprendiendo de por vida. Aplicando una aproximación científica, tanto para el desarrollo de producto como mejora de proceso, aprendiendo de aciertos y fallos. Además, transformando los aprendizajes locales en mejoras globales para toda la organización.

Sus principios son:

  • Facilitar el aprendizaje organizacional y una cultura de seguridad psicológica: Frente a buscar el error humano, buscar modos en la que se pueda rediseñar el sistema para que no vuelva a ocurrir este accidente.

    By removing blame, you remove fear; by removing fear, you enable honesty; honesty enables prevention. (Bethany Macri)

  • Institucionalizar la mejora del trabajo diario: Reservar tiempo explícito para pagar deuda técnica, arreglar defectos y áreas problemáticas.
  • Transformar descubrimientos locales en mejoras globales: Tratar de convertir la experiencia que obtienen equipos o individuos en conocimiento explícito para la organización para que sea fácil de utilizar (librerías, configuraciones, código…).
  • Inyectar patrones de resiliencia en el trabajo diario: Mejorando las operaciones diarias introduciendo tensión de forma intencionada buscando mejorar (chaos engineering, organizar game days).
  • Los líderes refuerzan la cultura de aprendizaje: Establecer objetivos que enmarquen los experimentos haciendo explícito el problema a resolver, la hipótesis, el método de testearla, la interpretación de los resultados y usar los aprendizajes en las siguientes iteraciones.

Junto la aplicación de las prácticas técnicas (pipelines de despliegue, testing automático, añadir telemetría y alertado, chaos engineering…) y mejorar en ellas; estos principios DevOps nos ayudarán a tener organizaciones donde al entregar valor de negocio antes, se hagan más predictibles y se reduzcan riesgos.

Lo que, en consecuencia, ayudará a reducir estrés en los equipos de tecnología y a que esas organizaciones sean lugares más habitables.