Evaluando Consumer-Driven Contract Testing con Pact

Pact es una librería con soporte en múltiples lenguajes de programación para hacer Consumer-Driven Contract Testing de integraciones vía HTTP y mensajería.

Desde que hace la tira Iván Loire me descubriera en un tuit un poco de rebote su existencia, había vuelto a cruzarse por mi radar en un par de ocasiones la posibilidad de evaluarlo, pero por unas razones u otras terminaba sin priorizarlo y caía en el olvido.

Hace unas semanas Alberto Gualis andaba preparando un taller de Introducción al contract testing con Pact para hacerlo en Bilbao Software Crafters. Él estaba empezando a probarlo para testesar los contratos entre aplicaciones frontends con sus respectivos backends y a raíz de eso estaba preparando el taller.

Ahora que compartimos oficina estuvimos discutiendo sobre ello, y enfrentándolo a otro tipo de soluciones como Dredd para testear los documentos de descripción de APIs, librerías de Record & Replay como VCR o Wiremock para hacer tests de integración con servicios externos guardando peticiones reales, etc. Así que al final me lió para echarle un cable para hablar de ese tipo de herramientas en el taller y complementar un poco más el contenido.

Esta fue la excusa perfecta para forzarme a hacer un spike para entenderlo y poder evaluarlo mejor. Hace unos pocos meses ya estuve pensando en darle un tiento por ver si tenía sentido introducirlo en Devengo para enterarnos rápido de si algún cambio entre el API y las apps móviles rompe con el contrato esperado.

¿Cómo funciona Pact?

La visión de Consumer-Driven Contract Testing de Pact es que los clientes o consumers son quienes generan el contrato, y luego el servidor o provider deberá comprobar si es capaz cumplir con él.

El flujo simplificando un poco es el siguiente:

  • Escribir los tests de integración del consumer usando alguna de las librerías de Pact para hacer stubs de las respuestas que se esperarían del provider.
  • Al lanzar los tests, si pasan en verde se genera un fichero JSON que describe el contrato esperado por el consumer. He dejado un ejemplo sencillote en gist.
  • En otro momento el provider lanza con Pact la verificación. Busca que pueda cumplir con lo descrito en ese JSON simulando las peticiones que haría el consumer y comprueba que puede responder lo esperado.

Gif explicando el flujo de trabajo de Pact

Al lanzar los tests de forma independiente sobre cada artefacto frente a hacerlo end to end tenemos un feedback más rápido, nos facilita el mantenimiento de esos tests y nos da mayor estabilidad. Todo esto sin perder confianza de que hemos roto el contrato entre los artefactos.

Integración con los pipelines de CI

Evidentemente si optamos por una solución así querremos integrarla en nuestro pipeline de integración continua.

Normalmente no sólo querremos que se validen los contratos cuando se suba un cambio al repositorio del provider, si no que si cambia el fichero que representa al contrato de un consumer queremos desde el provider se compruebe que se sigue cumpliendo ese contrato (o no).

Al generarse ficheros JSON planos no suena descabellado orquestar con un puñado de scripts y hooks estas comprobaciones, pero ya hay un proyecto dentro del paraguas de la fundación Pact que nos ayuda a resolver eso: Pact Broker.

En Pact Broker se guardan los contratos de los consumers y es donde los providers irán a validarlos, nos facilita el lanzamiento de webhooks y el trabajar con distintas versiones de los contratos.

Conclusiones por el momento

Sólo he trabajado con la parte de HTTP, no he hecho ninguna prueba con el soporte de mensajería.

Para mis pruebas he usado la gema de ruby tanto para el provider como para un consumer, además de la librería de JVM para otro consumer. Inicialmente me parece que deja un pelín verboso la preparación de los tests en ambos lenguajes, pero no he hecho aún el ejercicio de intentar esconder eso en algún tipo de helpers que mejorarían la legibilidad de los tests.

El DSL de la librería de JVM me pareció que dejaba código difícil de seguir para ver las estructuras de respuesta esperadas, luego vi que existe el Lambda DSL que mejora el asunto. Esto es porque aún habiendo buena y abundante documentación y ejemplos, algunos resultaron estar algo desfasados.

La integración de Pact Broker en nuestro pipeline de CI con Travis resultó bastante sencilla, para evitarme montar infraestructura propia lo hice con Pactflow que es un Pact Broker en SaaS. Para publicar o validar con Pact Broker usé la gema pact_broker-client y el plugin de gradle. En el travis.yml tocó añadir la publicación de los contratos de los consumers y la validación de los contratos en el provider; mientras que en pactflow configué los webhooks que disparan la build de travis del provider cuando el contrato cambia.

Aunque ofrece un flujo donde el consumidor puede empezar a trabajar sin que el proveedor esté disponible, el consumidor debería tener buena comunicación e influencia sobre lo que publica el proveedor. Claramente tanto para servicios externos como equipos que trabajen aislados es una herramienta que no aportará valor.

Y definitivamente creo que cuadra genial con una aproximación API first, acordando el equipo del proveedor con los de los consumidores cómo se va a hacer esa integración para poder empezar a trabajar en paralelo, asegurándonos así que estamos cumpliendo con lo que hemos acordado. Así que inicialmente pinta genial, pero está por ver cómo afecta al flujo del trabajo en el día a día y del contexto de cada uno.

Sobre Sistemas de Diseño

Estaba preparando otro post donde quería incluir una pequeña explicación de qué es un design system o sistema de diseño. Al ver que me iba extendiendo y se iba a diluir con el tema original, me he atrevido a intentar escribir sobre qué es y dar un poco mi visión sobre ello.


Hay multitud de compañías y organizaciones que han ido trabajando en sus sistemas de diseño, incluso hay muchos que han sido liberados como open source: El famosísimo Material de Google, Primer de Github, shibori3 y purple3 de Heroku, el de Atlassian, el del gobierno de UK… Así que es un tema que va surgiendo de forma recurrente hablando con gente dedicada o involucrada en la creación de productos digitales.

¿Qué es un sistema de diseño?

Por conversaciones en las que he participado y algunas lecturas sobre el tema, veo que hay interpretaciones algo distintas sobre qué es un sistema de diseño. Así que cuando tengo oportunidad de preguntarle a alguien sobre a qué se refiere con sistema de diseño encuentro que lo habitual es que se utilice como sustituto al de guía de estilo o al de librería de componentes UI. Incluso en algún caso aislado he encontrado que en realidad se referían a que tenían plantillas que alguien había hecho 😅.

Una guía de estilo o una librería de componentes son librerías de patrones perceptuales y/o funcionales, los artefactos más visibles de un sistema de diseño, pero no son el sistema de diseño en sí mismo.

Hasta donde yo sé no hay una definición canónica, yo uso como base la del libro Design Systems de Alla Kholmatova:

A set of connected patterns and shared practices, coherently organized to serve the purposes of a digital product.

Los patrones son una pata y las prácticas que se utilizan la otra, pero como en cualquier otro producto debemos marcar unas metas u objetivos además de tener una serie de principios de diseño que estén acordes, todo ello nos servirá como guía para la sistematización.

No darle un sentido claro a un sistema de diseño puede hacernos acabar con un batiburrillo de patrones que no tengan mucha consistencia entre sí, tener una variedad de patrones excesiva para resolver un mismo problema o incluso provocar conflictos entre personas por no tener claros los objetivos.

De cara a tener una comunicación efectiva entre los que construimos producto, también debemos trabajar en tener un lenguaje compartido para referirnos a los patrones del sistema de diseño para evitar confusiones.

Ya sean más descriptivos o algo más metafóricos, ese lenguaje compartido debería ser fácil de comprender por quienes construyen o utilizan el sistema de diseño para hacer producto. Deberíamos esforzarnos tratar de dar nombres que reflejen la finalidad de los patrones y no su aspecto.

Un ejemplo muy sencillito podría ser tener un patrón que se llame "call to action button" frente a "button orange big". Uno indica para qué está pensado mientras que el otro dice y está acoplado a cómo es, si cambiara algo de su aspecto su nombre perdería el sentido en el segundo caso.

Evidentemente todo esto termina en librerías de patrones para herramientas tipo Sketch, Figma, Adobe XD… enfocado a que quienes diseñen puedan reutilizarlos para poder centrarse más en la exploración de nuevos problemas que en resolver de nuevo cuestiones que ya se han resuelto anteriormente.

No tan habitual pero también necesario es tener artefactos como guías de componentes en html/css plano y librerías de componentes para el framework javascript o de la plataforma nativa de turno. Así se facilita el trabajo de quienes pasan a código frontend los diseños permitiendo entregar antes y ganando en mantenibilidad.

¿En qué nos beneficia todo esto?

  • Tener mayor consistencia tanto visual como de interacción. Al compartir principios de diseño facilitamos que los usuarios tengan una experiencia de usuario similar usando las distintas partes de un producto o productos que compartan y respeten el mismo sistema de diseño.
  • Mejorar la comunicación entre los miembros de un equipo de producto por tener un lenguaje compartido entre diseño, desarrollo, producto… En la documentación, UI kits, código, discusiones…
  • Gracias a las diferentes librerías con el tiempo permite ser más eficientes tanto al diseñar como al desarrollar. Esta eficiencia suele traducirse en permitir centrarse en resolver problemas nuevos y en entregar producto antes.

Suelo insistir en que a lo que desde luego no ayuda es a no contratar a alguien con skills de diseño, pretender que un sistema de diseño sea una bala de plata que cubra todo es bastante ingenuo. Por mucho sistema de diseño que haya, luego se ven interfaces que son auténticas aberraciones.

Además hay que tener en cuenta que un sistema de diseño debería estar en continua revisión, lo único constante es el cambio y haciendo producto digital el cambio es bastante rápido.

Domain-Driven Design "in a Shot"

Como parte de mi trabajo en Zara.com una de las cuestiones sobre la que trabajé con mis equipos fue en introducir conceptos de Domain-Driven Design, acompañado del estilo de arquitectura de Ports & Adapters (o Clean/Hexagonal Architecture). Había algunos productos que estábamos arrancando a los que se les presuponía que iban a tener una complejidad de negocio que iba a crecer, además tenían bastante incertidumbre inicialmente y se esperaba que su evolución perdurase en el tiempo al menos un puñado de años.

Empecé a intentar redactar de forma muy condensada un documento con los conceptos relacionados con DDD, por tratar de aligerar nuestras conversaciones sin necesidad de leerse los típicos libros azul, rojo o verde; que además suelen resultar durillos o incluso confusos.

Y aunque finalmente terminamos trabajando en los equipos practicando DDD y Ports & Adapters, no fui capaz de terminar con este documento. El día a día no me permitió dedicarle el tiempo necesario, así que aprovechando ese borrador he ido dedicando ratos posteriormente para terminar de aterrizarlo y servir como complemento a Diseño incremental de software a partir de las interacciones.

Foto de unos machacados, chupito especialidad de un garito de Zaragoza

Introducción

Domain-Driven Design (en adelante DDD) o diseño dirigido por dominio son un conjunto de prácticas que tratan de facilitar el desarrollo de software con un dominio complejo.

DDD se divide en artefactos a nivel estrátegico y táctico. El estratégico podríamos decir que se enfoca más al largo plazo y a lo funcional/dominio: especificaciones, documentación, algunas cuestiones de arquitectura de alto nivel… Mientras que el táctico se enfoca en el diseño más cercano a la implementación, a través de varios patrones y buenas prácticas.

Hay que tener en cuenta que DDD a nivel táctico consta de bastantes tipos de artefactos y que no es necesario utilizarlos todos de golpe y siempre, pero sí creo que es interesante conocerlos para poder sacarles partido en momentos dados.

En mi experiencia DDD casa muy bien tanto con las ideas de arquitectura de Ports & Adapters (más conocida como Hexagonal o Clean) para separar dominio/infraestructura/mecanismo de entrega, como con la práctica de TDD junto a prácticas de Specification by Example/ATDD.

TDD y Specification by Example/ATDD son prácticas más que recomendables. Nos ayudan a tener foco y a facilitar que emerga el diseño poco a poco a través de los flujos autosimilares de Red, Green, Refactor y Specify, Develop, Deploy.

Está fuera del ámbito de este documento la explicación en detalle tanto de estas prácticas como de Ports & Adapters.

Strategic Design

Dentro de los artefactos de Strategic Design se hace énfasis en dividir un problema grande en diferentes subdominios, con contextos delimitados o Bounded Contexts para hacerlo más manejable. Evidentemente, es habitual que distintos subdominios se relacionen entre sí usando distintos patrones de integración.

En cada contexto se establece un Ubiquitous Language basado en su subdominio para evitar equívocos entre cualquier miembro del equipo o stakeholders que participan en su desarrollo, todas las personas involucradas deberían utilizar las mismas palabras para referirse a los mismos conceptos de negocio.

El significado del concepto Product o Purchase en una gran empresa de retail puede tener docenas según el contexto en el que se usa. Pero dentro de un Bounded Context un concepto debe ser canónico, debe tener un único nombre y ese nombre un único significado.

Así que debemos tratar de utilizar un nombrado uniforme en todos los artefactos (código de producción, documentación, tests…) e incluso no está de más tener un glosario de términos como referencia de ese lenguaje ubicuo.

Para facilitar el entender cómo se relacionan los diferentes subdominos podemos visibilizarlo a través de un Context Map. Los context maps nos ayudan a ver qué subdominios existen, cómo se relacionan entre ellos organizativamente y los patrones de integración que hay.

Para quien quiera profundizar en esto, hay diferentes libros, charlas o artículos que entran en mucho más detalle. Por ejemplo: Strategic Domain Driven Design with Context Mapping.

Tactical Design

Los artefactos de Tactical Design son un conjunto de patrones que nos facilitan implementar en código el modelo de dominio dentro de un Bounded Context. Estos patrones pueden tener diferentes implementaciones, estar más o menos desacopladas de frameworks y librerías, etc.

Los modelos

Al modelar dentro de un subdominio encontramos conceptos que no se deben separar nunca, deben formar parte de la misma transacción siempre, estos conceptos son los Aggregates. Estos a su vez están compuestos de una o varias Entities y posiblemente de algunos Value Objects. Siempre habrá una Entity raíz en el Aggregate por la que se deberá acceder al resto de entities y value objects.

Podemos resumir qué es una Entity como algo que tiene un identificador único que lo distingue, aunque cambien el resto de sus atributos, el identificador jamás cambia. Deberíamos evitar tener entidades anémicas (sólo con getters y setters) tratar de empujar la lógica de negocio en lo posible hacia estos artefactos, siempre que no haya dependencias con colaboradores que no sean otras entities o value objects del aggregate.

Los Value Objects en cambio no tienen identidad y deberían ser inmutables por diseño (crearlo con un new y sólo métodos que consultan estado). Nos sirve para modelar conceptos en clases de dominio evitando tener responsabilidades repartidas por usar siempre tipos primitivos. Cuando hablamos de tipos primitivos incluiríamos también clases tipo String, colecciones estándar, etc.

La aproximación más purista lleva a que los atributos de una entity se modelen como value objets, personalmente prefiero empezar con tipos primitivos y si acaso emergen necesidades ir refactorizando a clases.

Un ejemplo muy simplificado de todo esto podría ser un agregado Invoice, dentro del dominio de una herramienta de facturación como este:

class Invoice {
  InvoiceId id; //Value object para encapsular el tipo primitivo
  Collection<InvoiceLine> lines; //Una lista de entity InvoiceLine
  CustomerId customerId; //Value object encapsulando la referencia a una entidad de otro agregado
  ...
  void addLine(InvoiceLine line){ //Método que modifica el estado de la entidad
  ...
}
class InvoiceId {
  String value; //Valor del tipo primitivo
  ...
  Boolean equals(InvoiceId id){
  ...
}
class InvoiceLine {
  InvoiceLineId id; //Value object para encapsular el tipo primitivo
  String description; //Tipo primitivo
  Money amount; //Value object para encapsular cantidad y divisa
  ...
}
...

La persistencia

Por otro lado, lo normal es que queramos persistencia, para lo que tendremos un Repository por cada Aggregate. Se encargará de esconder los detalles de la tecnología de persistencia y los mapeos que fueran necesarios.

Conceptualmente este patrón debería exponer métodos muy similares a una colección y ocultarnos los detalles de implementación: guardar un elemento, borrarlo, métodos básicos de consulta… aunque también es habitual ver expuestos métodos para queries más complejas tipo findAllByStatus o getByEmail frente al uso de query objects.

Los servicios

Como creo que a otras muchas personas, al ir introduciéndome en DDD me costó ver la distinción entre servicios. El que compartan parte del nombre resulta algo confuso, también ver cómo se refieren a la idea de servicios en documentaciones sobre frameworks y muchos artículos. ¿Qué diferencias hay entre Application Services y Domain Services?

Los Application Services son los puntos de entrada a la aplicación, los que orquestan y exponen el modelo de dominio. Personalmente prefiero referirme a ello como Use Cases para facilitar el explicar y entender su cometido; también los he llamado o visto llamar como Actions o Commands.

Sin entrar en mucho detalle a la hora de implementarlo, ya que hay muchísimas aproximaciones de hacerlo, mi preferencia es una clase por Use Case con un sólo método público expuesto. Siguiendo el ejemplo de una herramienta de facturación podríamos tener unas clases RegisterInvoice, AddLineToInvoice… con un método con nombre tipo run o execute.

Para evitar efectos colaterales, evitaremos tener use cases que se llamen entre ellos. Si aparece lógica común y estamos seguros de que estamos duplicando conceptos, podemos crear un Domain Service para actuar como helper.

Los Domain Services vienen a ser clases que encapsulan reglas de negocio que no tienen cabida para moverlos dentro de entities ni value objects. Suelen emerger porque hay reglas de negocio que involucran dependencias a ports o hay que orquestar varios aggregates diferentes.

Intentaremos que esas clases sean pequeñas y tengan una sola responsabilidad.

Sin ser un concepto original de DDD no quiero dejar mencionar la idea de Infrastructure Services, vi usarlo en algunos sitios y lo incorporé a mi terminología. Son simple y llanamente fachadas o clientes de servicios externos que no forman parte de mi dominio, así que los suelo nombrar como Client estilo SolrClient o GiphyClient.

Los eventos

Un Domain Events es algo significativo a nivel de negocio que ha ocurrido en el dominio que modelamos como una clase, tipo: InvoiceRegistered, ProductPublished, UserConfirmed… que contendrán los detalles de lo que ha ocurrido en el dominio para que sea publicado.

Personalmente suelo utilizarlos sólo al ver que el producto que estoy construyendo, además de complejidad, va a tener muchos side-effects o pueda interesar a otros subdominios: alimentar analítica para un dashboard, actualizar contadores, enviar un email, provocar algún trigger en un sistema externo…

La publicación de los eventos la dejaríamos encapsulada en un publisher, dejando a los subscribers la responsabilidad de comprobar si les interesa o no el evento y qué hacer con ello. Esos artefactos a nivel de infraestructura podrían implementarse con múltiples tecnologías: redis pub/sub, AMQP, kafka… O, mi preferencia para empezar a introducirlo, simplemente un patrón observer manejando todo en memoria.


Quienes conozcan Domain-Driven Design verán que he dejado algunas cosas fuera de este artículo y no he entrado a mucho detalle por tratar de condensar, ya que era algo inicialmente pensado para ayudar a asimilar los conceptos para gente que se inicia y acompañarlo con código de aplicaciones reales.

Con el mismo objetivo, personalmente me parece muy interesante un ejercicio similar que hicieron en video (muuuy rápido) los amigos de CodelyTV: DDD en 20 minutos.

Para profundizar más hay multitud de recursos en forma de charlas, libros, cursos…

Relacionados

Sobre el rediseño

Hace un par de meses publiqué el rediseño de la home de mi web personal y de paso de este blog.

Versión 2013

El anterior diseño, de José Luis Lizano y maquetado por Guillermo Latorre, databa de inicios de 2013 y estaba enfocado a ser un portfolio de algunos de mis trabajos como desarrollador freelance hasta ese momento. Tenía el objetivo de dar a conocer el tipo de trabajos para el que se me podía contratar, objetivo que había perdido sentido hacía tiempo.

A su vez el blog lo maqueté yo basándome en el trabajo que hicieron ellos en la home, aún siendo un resultado medianamente digno, se evidenciaban mis limitaciones.

Pantallazo el diseño previo del blog

Originalmente, tanto la web como el blog estaban sobre Wordpress en un hosting compartido, así que tocaba ir haciendo actualizaciones y mantenimiento de vez en cuando… Normalmente tarde y mal. Así que a mediados de 2017 lo migré a Jekyll en Github Pages, integrando Disqus para los comentarios y así poder olvidarme de eso. En ese momento me tocó andar revisando la parte responsive para que no se viera el diseño totalmente roto accediendo desde móviles, algo que hasta entonces no le había dedicado atención.

Versión 2019

Tras mucho tiempo con ello en la cabeza, finalmente nos pusimos a darle una vuelta con Vanessa Rubio al rediseño. Sin tener objetivos en sí mismos, las ideas principales eran:

  • Cambio en el enfoque de comunicación, quería que la home fuera algo más parecido a una carta de presentación. Romper con el enfoque de portfolio para pasar a contar qué cosas hago, qué he hecho, mis intereses, etc.
  • Ganar en legibilidad, ya que el anterior diseño de la home estaba planteado para textos cortos y la adaptación que hice para el blog arrastraba por ello algunos problemas. Quería volver a darle protagonismo a este blog escribiendo más en él y dejando de publicar en Medium.
  • Tener un diseño pensado para consumir contenido desde dispositivos móviles, por razones evidentes.

Boceto inicial del diseño del blog tanto versión escritorio como móvil

Para ponerlo en común trabajamos sobre bocetos en papel iterando rápido hasta llegar a la imagen de este post, que usó Vanessa como base para diseñar ya la parte visual entrando a detalle junto a contenido que fui evolucionando.

Una vez diseñada la parte visual, empezó a trabajar en la maquetación responsive sobre un fork del repositorio de git. Incorporando los cambios directamente en el Jekyll existente, con el contenido real y sincronizando alguno de mis cambios en cuanto a copys. Además, aprovechando que Jekyll lo soporta de serie, en el camino migró los estilos de Less a Sass.

Para finalizar, estas son algunas explicaciones de decisiones sobre el rediseño de Vanessa:

La idea era rediseñar sin perder la identidad actual. Para eso mantuve los principales elementos identificativos, como el logo, los colores y la tipografía.

Como queríamos facilitar la lectura de textos largos, le di más peso al blanco (sustituyéndolo como color de fondo) y dejé el negro principalmente para las zonas de navegación y la intro de la home. Para hacerla también más cómoda, especialmente en dispositivos móviles, aumenté el tamaño de fuente y del interlineado; añadiendo además espacios entre apartados para facilitar la lectura en diagonal.

Quería que fuera un espacio limpio, sin elementos que pudieran distraer la lectura. Para eso eliminé la barra derecha del blog ampliando así el espacio del cuerpo del post y dejé bastante aire alrededor para relajar la vista y centrar la atención en el contenido.

Por último, también quería destacar los CTAs más importantes del blog (como el de enviar comentarios o navegar entre páginas) convirtiéndolos en botones y unificando al mismo tiempo esos mismos estilos con los de la home para mantener la coherencia entre elementos similares.

Personalmente estoy muy contento con el resultado :)

KPIs para equipos de desarrollo de software

Como parte de mi trabajo en el último año, he intentado empujar la cultura de mejora continua en los diferentes equipos con los que he ido trabajando. Tanto en cuestiones de herramientas y habilidades técnicas, como en las de comunicación y coordinación, con ciertas restricciones y dependencias que caen fuera de nuestro margen de influencia.

Partimos del supuesto de que cuanto mejores sean las prácticas del equipo, mejor capacidad de entrega tendrá. Eso se traduce en una mayor adaptabilidad a los cambios, una mejor mantenibilidad del software con el paso del tiempo y posiblemente una mayor motivación del equipo con su trabajo.

Además de las típicas sensaciones subjetivas de efecto de mejora, teníamos que pensar en indicadores que nos permitieran ir observándolo realmente. En algún momento, también se empezaría a querer tener visibilidad desde fuera de los equipos, así que tocaba darle una vuelta y ordenar ideas.

Estuve preparando una presentación para explicar internamente cómo estábamos trabajando, hacia dónde creía que debíamos ir a través de una mejora continua, los distintos KPIs que podríamos observar y las necesidades que se cubrirían con una buena capacidad de entrega.

Diapositiva de una presentación con la visión de lo que, como equipo deberíamos cubrir: adaptabilidad respecto a negocio, entregar pronto, evitar retrabajo, una buena UX y evitar bugs en lo posible

“Dime cómo me mides y te diré cómo me comporto”

Desconozco el origen, pero con estas cosas siempre me acuerdo de este dicho. Hay que andar con ojo con qué indicadores (que no objetivos) vamos a medir. Además, tendremos que usar varios para compensar el fomento de comportamientos extraños que falsean esas métricas.

Con el uso normal de las herramientas, sólo acordando algunas convenciones, se puede facilitar la explotación de datos posterior para extraer métricas. Los orígenes de datos para los indicadores son:

  • El código
  • El repositorio y servidor de automatización
  • Las herramientas de gestión
  • El propio producto

Indicadores del código

Las herramientas de análisis estático de código nos dan números sobre deuda técnica que existe. No debemos perder de vista estos indicadores y dedicar tiempo en analizar y hacer limpieza específica de vez en cuando. Unas veces se resuelven con soluciones simples y otras esconden problemas de diseño que no resultan tan evidentes.

La cobertura de test es uno de los indicadores más habituales, lo más interesante en este caso es ver qué NO está cubierto. Y como se suele comentar, hay que andar con cuidado porque es un indicador fácil de falsear si se busca como objetivo.

En un momento dado, además, se podría utilizar mutation testing (comprobar que se rompe algún test al modificar código de producción) para tener un indicador de la calidad de los tests unitarios.

Indicadores del repositorio y servidor de automatización

Hay bastantes indicadores interesantes que se pueden sacar de los repositorios y servidores de automatización, aunque en su mayoría son dependientes de las convenciones de uso.

Sin embargo, un indicador siempre válido y que pienso que debería observarse es la frecuencia de integración.

La integración continua es una práctica (que no herramienta) tan popular como malinterpretada, ya que hasta que no se une el trabajo que ha hecho o está haciendo una persona con la rama principal de desarrollo y se completa una build, no la estamos realizando.

Hasta que no ha terminado correctamente la construcción de un artefacto de software, no sabemos si todo está correcto. A mayor frecuencia, feedback más temprano y menor incertidumbre.

En caso de usar ramas, también es interesante ver la duración de vida de las ramas. A más tiempo, mayor riesgo de conflictos u otros problemas al integrar.

Y si se utilizan pull/merge requests, también hay un puñado de indicadores que en un momento dado puedan sacarnos olores relacionados con la capacidad de entrega: cantidad de comentarios, tiempo que quedan abiertas, cantidad de rechazos…

Indicadores de las herramientas de gestión

Las herramientas de gestión, además de servir como radiador de información para saber la situación actual de la construcción del producto y ayudar a coordinar el trabajo, son una buena fuente de información de indicadores del proceso de trabajo.

Para observar la capacidad de todo el equipo en conjunto de hacer vertical slicing es muy útil conocer el tiempo de ciclo. A menor tiempo de ciclo, mayor es nuestra capacidad de entrega. Es el tiempo que se tarda desde que se empieza a trabajar en algo que aporte valor (por ejemplo, una historia de usuario) hasta que pasa a estar hecho.

Es habitual que en algún punto de las herramientas de gestión se pueden observar los distintos despliegues que se han realizado, donde podamos obtener la frecuencia de despliegue. Evidentemente a mayor frecuencia, mejor.

Aunque nuestro tiempo de ciclo fuera corto y la frecuencia de despliegue alta, sería posible que nuestro producto fuera frágil debido a bugs. Por eso el indicador de bugs detectados y resueltos por versión/despliegue es otra métrica a tener siempre en cuenta.

Dependiendo del momento y escenario en el que se encuentre un producto, también me parece muy interesante el indicador del lead time, el tiempo que pasa desde que se pide algo nuevo hasta que está desplegado en producción. Que vendría a ser consecuencia de los 3 anteriores indicadores y del tamaño de la pila de producto.

Como supongo que haya quien pueda echarlo de menos, omito intencionadamente los indicadores al respecto de las estimaciones, tipo story points por iteración. En mi opinión, tienen un componente muy subjetivo y variable para ser utilizado como indicador de cambio en la capacidad de entrega de un equipo.

Indicadores del propio producto

Además de otro tipo de instrumentación mucho más minuciosa que necesitan los miembros del equipo de gestión de producto o diseño, el equipo de desarrollo debería poder observar el número y porcentaje de uso por funcionalidad, que al final define el éxito o no del trabajo de todos.

Lo más interesante de este indicador es que combinado con otras métricas puede ayudar a tomar decisiones sobre la evolución del producto. Como por ejemplo, este escenario:

  1. Detectamos problemas muy graves de rendimiento en una parte del producto.
  2. Observamos que el porcentaje de uso de la funcionalidad afectada por esos problemas es residual.
  3. Decidimos no resolverlo de momento, pero lo reflejamos en la pila del producto como algo poco prioritario.
  4. Configuramos una alerta para detectar cierto aumento en el porcentaje de uso de esa funcionalidad.

Otro indicador importante es el crash rate del producto, cuántas veces se detecta un fallo o error por cantidad de uso, que nos permite saber lo estable que es un producto. Y mezclado con el indicador de números de uso por funcionalidad, nos permite detectar los puntos problemáticos.

Dependiendo del contexto del producto y del negocio, posiblemente podamos sacar también otros indicadores relevantes de operaciones y soporte que sean consecuencia del uso del producto.

Medir sin perder el foco

Estos son muchos KPIs distintos. Es interesante observarlos porque nos pueden servir para detectar olores sobre problemas y oportunidades de mejora, pero son demasiados para tratar de mejorar todo a la vez.

Para evitar diluirnos y no terminar mejorando en nada, deberíamos elegir y enfocarnos en un par de esos indicadores cada vez, dependiendo de la situación de cada equipo.

Y aunque sea tentador hacerlo, evitaría usar alegremente estos indicadores para marcar objetivos, así como para evaluar a equipos distintos.

No olvidemos que el propósito de medir estos indicadores es observar la evolución en el tiempo de un equipo trabajando en un producto y contexto determinado.