<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://danilat.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://danilat.com/" rel="alternate" type="text/html" /><updated>2026-06-16T22:01:25+00:00</updated><id>https://danilat.com/feed.xml</id><title type="html">Arragonán - Dani Latorre</title><subtitle>Dani Latorre – Desarrollador y algunas cosas más</subtitle><entry><title type="html">Intentando recuperar rutina</title><link href="https://danilat.com/weblog/2026/06/16/recupuerar-rutina" rel="alternate" type="text/html" title="Intentando recuperar rutina" /><published>2026-06-16T00:00:00+00:00</published><updated>2026-06-16T00:00:00+00:00</updated><id>https://danilat.com/weblog/2026/06/16/recupuerar-rutina</id><content type="html" xml:base="https://danilat.com/weblog/2026/06/16/recupuerar-rutina"><![CDATA[<p>Llevo unos meses en los que a ratos me entran ganas de publicar algo, pero luego me lío con otras cosas y lo voy dejando pasar, así llevo desde Octubre. En su momento quería escribir algo sobre mi incorporación a <a href="https://mito.ai/">Mito</a> como <em>team contributor</em>, pero como fui dejando pasar el tiempo fue perdiendo momentum para mi.</p>

<p>No pretendo escribir cosas hiper curradas, sólo intentar obligarme a ordenar ideas y plasmarlas por escrito intentando comunicarlas razonablemente bien. Y todo esto sin torturar ningún LLM, más allá de que me revise algún error gramatical o me dé feedback para ir mejorando mi redacción. Porque <a href="https://www.nature.com/articles/s44222-025-00323-4">writing is thinking</a>.</p>

<p><img src="/img/posts/2026/06/cuaderno.jpg" alt="Foto de un escritorio donde hay un teclado, un ratón, un taco de post its, un par de cuadernos pequeños y un boli" title="El escritorio listo para pasar a limpio nota tomadas a mano" /></p>

<p>Voy a seguir publicando aquí frente a plataformas de terceros por eso de <a href="https://www.theguardian.com/technology/2025/sep/28/why-i-gave-the-world-wide-web-away-for-free">la web abierta y tal</a>. Porque aunque en alguna ocasión del pasado me he currado mucho un artículo y eso a mi ego le gusta que tenga alcance, prefiero hacerlo bajo mi dominio aún a sabiendas de que va a tener infinitamente menos tráfico que los substacks, linkedins o mediums de turno</p>

<p>Como siempre en este blog mi intención es seguir publicando sobre temas relacionados con el mundillo del desarrollo de productos de software. Tengo algunos borradores empezados, algunos desde hace muchísimos y otros razonablemente recientes. De cualquier modo se aceptan sugerencias.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Llevo unos meses en los que a ratos me entran ganas de publicar algo, pero luego me lío con otras cosas y lo voy dejando pasar, así llevo desde Octubre. En su momento quería escribir algo sobre mi incorporación a Mito como team contributor, pero como fui dejando pasar el tiempo fue perdiendo momentum para mi.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://danilat.com/img/posts/2026/06/cuaderno.jpg" /><media:content medium="image" url="https://danilat.com/img/posts/2026/06/cuaderno.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Viviendo en el péndulo. Hasta la vista Genially</title><link href="https://danilat.com/weblog/2025/10/13/viviendo-pendulo-hasta-la-vista-genially" rel="alternate" type="text/html" title="Viviendo en el péndulo. Hasta la vista Genially" /><published>2025-10-13T00:00:00+00:00</published><updated>2025-10-13T00:00:00+00:00</updated><id>https://danilat.com/weblog/2025/10/13/viviendo-pendulo-hasta-la-vista-genially</id><content type="html" xml:base="https://danilat.com/weblog/2025/10/13/viviendo-pendulo-hasta-la-vista-genially"><![CDATA[<p>Tras casi 4 años, esta fue mi última semana en <a href="https://genially.com/">Genially</a>. Una decisión que me costó mucho tomar tanto por motivos personales, con un ambiente de compañerismo en la compañía que creo que no es habitual en otros lugares que tienen cierta escala y haber construido lazos con un buen puñado de personas; como por motivos profesionales, con unos cuantos desafíos que se venían a corto y medio plazo en los que sé que hubiera disfrutado y aprendido.</p>

<p>En este tiempo, mi rol en la empresa se fue transformando poco a poco casi sin darnos cuenta, primero influenciando solo a nivel de tecnología y terminando haciéndolo a nivel de producto, negocio y operaciones. Hubo algo que fue una constante: <strong>vivir en el péndulo</strong> <a href="https://www.infoq.com/presentations/hands-on-coding-managers/">entre individual/team contributor y el management</a>.</p>

<h3 id="incorporación">Incorporación</h3>

<p>Entré casi por casualidad. Conocí a <a href="https://www.linkedin.com/in/chemaroldan">Chema</a>, CTO de Genially, unos años antes en <a href="https://2019.pamplonaswcraft.com/">una Pamplona Crafters</a> y mantuvimos el contacto vía redes sociales (cuando <em>tuiter</em> aún molaba). Tiempo después, hablamos por otro tema y me propuso explorar si tenía sentido incorporarme. La compañía venía de crecer mucho y vieron que podía asumir un rol que resultase complementario.</p>

<p>Justo era un momento en el que empezaba a sentir que necesitaba un cambio profesional, pero quería estar seguro de mi siguiente paso. Así que tras haber hecho el proceso de selección, nos tomamos un par de meses y tuvimos varias conversaciones hasta que finalmente me incorporé a la compañía.</p>

<h3 id="plataforma-y-organización">Plataforma y organización</h3>

<p>Entré con un rol de <em>Platform Lead</em>, por lo que mi responsabilidad iba a ser ayudar a los equipos de producto a que pudieran enfocarse en <strong>tener impacto</strong>, tratando de mejorar la <strong>experiencia de desarrollo</strong> y la <strong>efectividad</strong> de los equipos. Como bonus, también iba a echar una mano en cuestiones más organizativas.</p>

<p>Aunque este era un rol de liderazgo, no había un equipo dedicado, de modo que iba a tener que influir, colaborar con distintos equipos y empujar algunas iniciativas por mi cuenta.</p>

<h4 id="sobre-el-trabajo-de-plataforma">Sobre el trabajo de plataforma</h4>

<p>Una vez hecho el onboarding, empezar a tener algunas iniciativas ya lanzadas y conociendo más la compañía, traté de aterrizar una serie de metas:</p>

<blockquote>
  <p><em>Las iniciativas y actividades de plataforma deben facilitar la Developer Experience y ayudar a los equipos en distintos aspectos:</em></p>
</blockquote>

<blockquote>
  <ul>
    <li><em>Reducir su carga cognitiva, que se enfoquen en complejidad intrínseca y no en la extrínseca, potenciando la <a href="https://es.wikipedia.org/wiki/Carga_cognitiva#Carga_alemana">germana</a> (generando aprendizaje, construyendo esquemas o automatizando su uso)</em></li>
    <li><em>Habilitar actitudes data-informed en la toma de decisiones</em></li>
    <li><em>Aportar la mayor autonomía y empoderamiento posibles</em></li>
    <li><em>Tratar de ser lo más eficientes en cuestiones de costes económicos</em></li>
    <li><em>Dar fiabilidad, que las herramientas apenas fallen</em></li>
  </ul>
</blockquote>

<p>Metas que traté de tener en cuenta, definiendo e implantando prácticas, procesos y herramientas, automatizando tareas, creando documentación… Y darles seguimiento unas veces de manera cuantitativa y otras de forma cualitativa.</p>

<p>Algunas de las iniciativas más relevantes fueron estas:</p>

<ul>
  <li>Actuar como puente entre el equipo de infraestructura y los equipos de producto para acelerar la implantación del stack de observabilidad de Grafana (Loki, Mimir, Tempo).</li>
  <li>Formalizar la redacción de postmortems <em>blameless</em> tras incidencias que afecten al servicio junto a CTO y VP de ingeniería, para fomentar el aprendizaje sistémico.</li>
  <li>Apoyar al equipo de Design System con distintas actividades.</li>
  <li>Ayudar en la introducción de Snowplow + Amplitude para la instrumentación de producto colaborando con el equipo de data y los primeros equipos de producto involucrados.</li>
  <li>Introducir el uso de Turborepo en el monorepo para simplificar el proceso de build.</li>
  <li>…</li>
</ul>

<h4 id="a-nivel-organizativo">A nivel organizativo</h4>

<p>Durante esos primeros meses se estaba planteando una <strong>reorganización del área de desarrollo de producto</strong> bastante importante. Básicamente era pasar de una estructura de <em>squads</em> que se organizaba de forma recurrente a una basada en <em>líneas de trabajo</em> que tuvieran continuidad en el tiempo.</p>

<p>Esto fue algo en el que me involucraron aún sin tener mucho contexto junto al resto de managers de tech y producto, en parte venía bien tener a alguien con mirada limpia aunque en ocasiones pudiera pecar de ingenuo, para cuestionar supuestos y detectar disonancias.</p>

<p>En aquel entonces me resultó muy útil que alguna de la gente involucrada en la reorganización estuviera hablando en términos de <em>Team Topologies</em>, porque nos ayudaba a tener un lenguaje común. Eso lo terminamos enfrentando con un ejercicio de <em>Context Mapping</em> (<a href="https://danilat.com/weblog/2024/05/08/commitconf">en un par de eventos charlé sobre ello</a>), donde representamos buena parte del funcionamiento de la compañía para ver el encaje de equipos respecto a la situación actual.</p>

<p>Este ejercicio sirvió para completar la foto real a la que íbamos, e incluso permitió detectar potenciales problemas de <em>bounded contexts</em> en los que había muchas manos y algunas en las que había muy pocas. Así que cuando con el paso del tiempo surgieron algunos problemas relacionados, no nos pilló tan por sorpresa.</p>

<p>Además, institucionalizamos el dar seguimiento a lo que surgía de las retros de los equipos, ya que se estableció como práctica que se documentase por escrito al menos lo más destacado de ellas y las acciones que surgieran. Esta práctica se mantiene a día de hoy, y resulta muy útil a la capa de management como complemento a los <em>1:1s</em> para detectar fricciones y puntos de mejora.</p>

<p>Mucho de esto y algunas cosas más, las compartí en la charla <a href="https://danilat.com/weblog/2025/03/09/scpna-desarrollo-producto-scaleup">Desarrollo de producto en una Scale-Up</a> precisamente en la Pamplona Crafters 2024.</p>

<p><img src="/img/posts/2025/hasta-la-vista-genially/team.jpg" alt="Foto donde hay algo más de 100 personas de pie posando para la foto, fondo con árboles y suelo adoquinado" title="Foto del team building de toda la compañía en el mes de Junio de 2025, aunque no está todo el mundo hay representación de todas las áreas de la compañía" /></p>

<h3 id="volviendo-a-hacer-producto-interno">Volviendo a hacer producto (interno)</h3>

<p>Tras un año, mi foco cambió a ser algo más todoterreno. Por un lado, ya tenía bastante controladas las dinámicas en el área de desarrollo de producto y a nivel de plataforma había aún puntos de mejora en los que iba trabajando, pero estábamos ya más en una fase de mejora continua y constante, además el equipo de infraestructura seguía introduciendo grandes mejoras. Mientras tanto, en otras áreas de la compañía se identificaban problemas mayores con los que creíamos que podía aportar más.</p>

<h4 id="ventas">Ventas</h4>

<p>Uno que terminó surgiendo fue en el equipo de ventas, al que había que buscarle solución de forma prioritaria, ya que ninguna de las líneas de trabajo a priori tenía foco en ese tema. Teníamos un <strong>proceso interno ineficiente y propenso a errores</strong> que terminaban sufriendo clientes High Touch de gran tamaño: muchos emails y llamadas de coordinación, uso de spreadsheets de gestión, tiempos de espera por parte de clientes y account managers, etc.</p>

<p>Para solventar el problema se decidió montar un <em>squad</em> específico moviendo a algunos perfiles técnicos desde algunas líneas de trabajo, y como no queríamos sacar de foco a product managers terminé involucrado en la fase de discovery para abordarlo.</p>

<p>Así que tocó entender los problemas que el equipo de ventas se estaba encontrando y, junto a compañeras de customer experience, entrevistarnos con clientes y gente del área de ventas. A partir de ahí, documentar el impacto que estaba teniendo, definir un primer alcance del MVP usando un <em>user story map</em> y preparar un kick-off para tener tanto al squad, stakeholders y el resto de la compañía alineados.</p>

<p>El squad trabajó en una herramienta que diera mayor autonomía a esos clientes de gran tamaño, permitiendo a account managers mantener el control y darles seguimiento. Y en mi caso, aunque no tiré ni una línea de código y que luego me quedase acompañando al squad de lejos casi como un stakeholder más, me lo pasé como un enano volviendo a participar en hacer producto en fases iniciales.</p>

<h4 id="creativo">Creativo</h4>

<p>Tiempo más tarde, nos encontrarnos otro problema importante en el área de creativo. Uno de los valores que ofrece Genially a las personas usuarias son las plantillas diseñadas por este equipo. Nos encontramos con un problema de atasco en la publicación de nuevas plantillas y cambios en las existentes que antaño no ocurría, el producto había evolucionado de un modo en el que terminó afectando en la operativa diaria de este equipo.</p>

<p>Esto es porque una vez diseñadas, existía un <strong>cuello de botella en el proceso de publicación</strong> tanto en el producto como en la web pública. Esta ineficiencia en el <em>Go to Market</em> provocaba tardar más tiempo en recuperar la inversión, era propenso a errores y frustraba a las personas de ese equipo.</p>

<p>Al final era un problema para el que la <strong>perspectiva Lean</strong> encajaba como anillo al dedo: Identificar el valor, mapear el flujo de trabajo, mantener un flujo continuo, aplicar sistema pull y buscar la mejora continua. Para lo cual se decidió crear de nuevo un <em>squad</em> que se enfocara en esta área.</p>

<p>Una vez analizado y mapeado el journey principal que queríamos resolver, habiendo identificado los distintos hand-offs y limitaciones de las herramientas, planteamos crear un nuevo backoffice diseñado para habilitar mayor autonomía y que simplificase su proceso. De ese modo podríamos sustituir de forma incremental el backoffice legacy, un CMS y un par de spreadsheets de gestión.</p>

<p>Para acelerar el proceso de publicación introdujimos: soporte i18n, gestión de estados, uso de IA generativa, mejoras en las validaciones… Además de crear un servicio que pudiera consumirse desde el producto y la web, cosa que evitaba el uso de herramientas externas y, a nivel técnico, simplificaba la infraestructura y la mantenibilidad futura.</p>

<p>Una vez eliminado ese cuello de botella, que con la combinación del trabajo del squad con el equipo creativo pasó de un retraso de <strong>4 meses</strong> a estar <strong>al día</strong>, nos centramos en mover y mejorar el resto de procesos que se soportaban aún en el legacy en esta nueva herramienta, y en el camino colaborar con uno de los equipos de producto para introducir algunas mejoras conjuntamente.</p>

<h3 id="operaciones-y-tech">Operaciones y tech</h3>

<p>Aproximadamente el último año en Genially mi rol terminó pivotando de manera oficial a trabajar con foco en las operaciones internas de la compañía. Esto significaba estar en un punto medio entre <strong>tecnología, producto, negocio, organización</strong> y su potencial impacto en la operativa diaria de cualquier área de la compañía. Esto implicaba mucha amplitud y normalmente menor profundidad, mucha comunicación, intentar promover iniciativas que tuvieran sentido o frenar las que aparentemente necesitasen reflexionarse más, identificar posibles problemas entre áreas de forma prematura, moverme todavía más habitualmente entre diferentes grados de abstracción, etc.</p>

<p>Siempre con una <strong>perspectiva de desarrollo de producto</strong>, durante ese tiempo tuve oportunidad de trabajar de nuevo y esta vez mucho más involucrado con el área de ventas y desarrollo de negocio; además de empezar a hacerlo también con las de financiero, personas, soporte y comunidad.</p>

<h4 id="trabajando-con-más-áreas">Trabajando con más áreas</h4>

<p>Con el área de ventas empezamos a trabajar en <strong>hacer crecer el MVP</strong> que arrancamos antaño, soportando nuevas casuísticas primero y luego acercando e integrando esta herramienta con el CRM que se usa en la compañía. En mi caso había estado involucrado en el día a día de la línea de trabajo que lo evoluciona, con un rol de facilitador y ocasionalmente de desatascador.</p>

<p>Junto a las áreas de financiero y personas hicimos pequeñas <strong>iniciativas coordinadas con líneas de trabajo</strong> de producto, aunque algunas más ambiciosas se quedaron en el tintero porque el coste de llevarlas a cabo las hacían inviables al menos en el medio plazo.</p>

<p>Con soporte empecé a trabajar muy de cerca, ya que había una <strong>tormenta perfecta</strong> de cambios en el producto, iteraciones de negocio, atasco en los tiempos de respuesta y degradación de la experiencia de cliente.</p>

<p>Lanzamos varias acciones para solventar la situación: mejorar flujos en el chatbot, rehacer la integración con la herramienta de soporte para enriquecer la información de clientes y mejorar la gestión de colas, introducir automatizaciones, contratar e integrar a un proveedor para pasar a tener un chat basado en agentes de IA que escalase sólo casos complejos, etc. Una vez recuperada una situación de normalidad pudimos entrar en un modo de mejora continua en soporte, y poder dedicar más tiempo a iniciativas relacionadas con comunidad.</p>

<p>Y en medio de todo esto apoyar en ajustes organizacionales, tanto a nivel de desarrollo de producto como en el resto de áreas; y en iniciativas transversales, por ejemplo y para sorpresa de nadie, últimamente con foco en un par relacionadas con IA generativa.</p>

<p><img src="/img/posts/2025/hasta-la-vista-genially/impact-day.jpg" alt="Foto donde aparecemos 12 personas posando de pie, sucios y con barro en el suelo tras haber estado limpiando una casa en las afueras de Álora, Málaga, tras la DANA 2025 que también afectó a esa zona" title="Día de Impacto 2025 en Álora, Málaga. Es un día que patrocina Genially para que quienes trabajan ahí puedan echar una mano a ONGs, asociaciones, etc." /></p>

<h3 id="conclusión">Conclusión</h3>

<p>Aunque al inicio costó un poco ver cómo gestionar ese tipo de <strong>rol pendular</strong> en el que a veces era difícil manejar la cantidad de cambios de foco y de niveles de abstracción, se me dio siempre confianza y autonomía. Finalmente me sentí muy cómodo con ese tipo de responsabilidades.</p>

<p>Me permitió poder influir a nivel organizativo <strong>como un manager</strong> primero a nivel de área y luego tratar de hacerlo a nivel de compañía, aunque sin las responsabilidades de gestión de personas. Y de vez en cuando <strong>poder ejecutar trabajo directamente</strong> por ejemplo programando, documentando o investigando de forma autónoma o colaborando con más personas en equipo.</p>

<p>Tras tener la oportunidad de trabajar con tanta <strong>gente diferente y variedad de áreas</strong> en el contexto de una <em>scale-up,</em> cierro esta etapa en Genially habiendo crecido mucho profesionalmente y con un montón de aprendizajes: técnicos, organizativos, de personas, de negocio… y <strong>siendo un fan</strong> de la marca y la compañía.</p>

<h4 id="y-ahora-qué">Y ahora ¿qué?</h4>

<p>Toca arrancar una aventura nueva que no esperaba, uno de esos trenes que sientes que son una gran oportunidad que tienes que tomar, pero eso lo cuento un poco más adelante en otro post.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Tras casi 4 años, esta fue mi última semana en Genially. Donde mi rol fue transformándose, siendo una constante el movimiento entre el management y la contribución a nivel de equipo o individual]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://danilat.com/img/posts/2025/hasta-la-vista-genially/team.jpg" /><media:content medium="image" url="https://danilat.com/img/posts/2025/hasta-la-vista-genially/team.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Jugando con MCP protocol. Añadiendo bus, bizi y geocoding a MCP DNDzgz</title><link href="https://danilat.com/weblog/2025/05/13/jugando-mcp-2-bus-bizi-geocoding" rel="alternate" type="text/html" title="Jugando con MCP protocol. Añadiendo bus, bizi y geocoding a MCP DNDzgz" /><published>2025-05-13T00:00:00+00:00</published><updated>2025-05-13T00:00:00+00:00</updated><id>https://danilat.com/weblog/2025/05/13/jugando-mcp-2-bus-bizi-geocoding</id><content type="html" xml:base="https://danilat.com/weblog/2025/05/13/jugando-mcp-2-bus-bizi-geocoding"><![CDATA[<p>Tras <a href="https://danilat.com/weblog/2025/04/21/jugando-mcp-dndzgz">mis primeras pruebas jugando con MCP</a> sólo con la información del tranvía de Zaragoza, decidí dar el siguiente paso y añadir los otros servicios que históricamente ha soportado <a href="https://www.dndzgz.com/">DNDzgz</a>: <strong>autobús urbano y el servicio bizi</strong>, el alquiler de bicicletas municipal. Y con eso completar el soporte de los tres servicios públicos de movilidad más esenciales y usados en el día a día de las personas que viven o visitan la ciudad.</p>

<p>El soporte al servicio de bus en DNDzgz llevaba roto desde un cambio de contrata, por el que un scraper llevaba tiempo sin funcionar. Eso lo descubrí usando la versión web móvil en una de mis últimas visitas a Zaragoza, pero no le había dedicado tiempo hasta ahora. Una vez resuelto ese problema, volví a jugar con las tools de MCP usando Cursor para ir modificando y probando.</p>

<h3 id="añadiendo-el-soporte-de-bus-y-bizi-encontrando-límites">Añadiendo el soporte de bus y bizi, encontrando límites</h3>

<p>La implementación fue bastante directa, al ser algo tan sencillo le iba pidiendo al agente de Cursor que me generase el código para las tools de servicio de bizi y bus, tanto traer las posiciones como obtener la respuesta de los datos de estimación o disponibilidad en tiempo real.</p>

<p><img src="/img/posts/2025/mcp2/1-bizi.png" alt="Captura con el agente de Cursor respondiendo estimaciones en la parada de Plaza San Francisco" /></p>

<p>Sobre la marcha se me ocurrió que ya que el API de DNDzgz tiene información geolocalizada, podría estar bien un modo de conocer la ubicación rápidamente, así que añadí una nueva tool para que genere un enlace a google maps usando las coordenadas.</p>

<p><img src="/img/posts/2025/mcp2/2-google-maps.png" alt="Captura del agente de Cursor dando enlace a google maps para llegar a una estación de bizi y la disponibilidad de bicis" /></p>

<p>El problema apareció con el servicio de autobuses, que daba un error por terminar siempre con una conversación excesivamente larga. Esto se debía a que esta llamada devolvía una respuesta con un array JSON con unos pocos miles de objetos, cosa que tampoco es lo ideal pensando en los costes que tienen asociados estos LLMs con el consumo de tokens. Y como las pruebas siempre las he ido haciendo con cuentas gratuitas, esto se hizo evidente con este escenario.</p>

<p>Por ejemplo por parte de Cursor no encontré documentación del límite pero la respuesta está clara. <em>“Your conversation is too long”</em></p>

<p><img src="/img/posts/2025/mcp2/3-error-bus.png" alt="Captura con error al obtener paradas de bus con el agente de Cursor" /></p>

<p>Mientras Claude Desktop daba un error más raro, sobre que la respuesta fue interrumpida. Pero en su caso sí lo tienen documentado: <a href="https://support.anthropic.com/en/articles/7996848-how-large-is-claude-s-context-window">How large is Claude’s Context Window?</a></p>

<p><img src="/img/posts/2025/mcp2/4-claude-error-bus.png" alt="Captura con error al obtener paradas de bus en Claude Desktop" /></p>

<h3 id="tratando-de-reducir-el-tamaño-de-respuesta">Tratando de reducir el tamaño de respuesta</h3>

<p>Así que para salir del paso empecé a pensar cómo aligerar el tamaño de respuesta intentando no tener que tocar nada del API de DNDzgz, limitando los cambios al MCP server.</p>

<p>Como primer paso traté de ajustar la respuesta a lo mínimo necesario. Quitar el único atributo de la respuesta que no se usaba y filtrar resultados de paradas que sabía que no tienen realtime, ya que son del <a href="https://www.consorciozaragoza.es/">transporte metropolitano</a> y no <a href="https://zaragoza.avanzagrupo.com/">del urbano</a>. Esto se podía intentar identificar con las líneas en cada parada al seguir esta patrones diferentes en su nomenclatura.</p>

<p>Eso era un poco cutre y aún así el tamaño de respuesta se mantenía muy alto. Debía intentar encontrar una manera de afinar lo máximo posible lo que devolvía MCP DNDzgz para evitar responder con cientos o miles items en las peticiones recibidas.</p>

<p>Con eso en la cabeza lo dejé reposar durante unos días, finalmente se me ocurrieron otras 2 soluciones:</p>

<p>En un primer momento pensé en la posibilidad de tratar de montar un filtro sobre texto para devolver el mínimo de estaciones o paradas posible. Dada la interfaz conversacional tenía la sensación que todo lo que no fuera una búsqueda de vectores para hacerlo de un modo semántico podía resultar una experiencia de usuario mediocre. Como es algo con lo que ya he experimentado un poco en otras pruebas de concepto, sé que podría haber jugado con el <a href="https://js.langchain.com/docs/integrations/vectorstores/memory/">vector store en memoria de LangChaning</a> y usar para los <a href="https://js.langchain.com/docs/integrations/text_embedding/">embeddings</a> un proveedor externo o incluso añadir dependencia a Ollama.</p>

<p>Más tarde se me ocurrió otra opción, la búsqueda semántica es buena opción si sabes más o menos <strong>qué andas buscando</strong>. Pero dada la naturaleza de geolocalización de los servicios de movilidad lo más importante es el <strong>dónde lo andas buscando</strong>. Así que tal como están los datos expuestos en DNDzgz, veía que tenía más sentido ir por el camino de añadir una nueva tool que resuelva localizaciones y luego hacer búsquedas por posición. Esto parecía tener mucho sentido y estaba alieneado con el comportamiento que había visto en varias ocasiones de Claude, en el que el modelo me iba ofreciendo estimaciones en posiciones cercanas.</p>

<h3 id="solución-desde-la-experiencia-de-uso-de-dndzgz">Solución desde la experiencia de uso de DNDzgz</h3>

<p>Parece que tiene bastante sentido pedir cosas como “¿<em>Dónde puedo coger el 30 en la zona de Paseo de la Independencia?</em>”, “<em>Estoy en el Parque Bruil, ¿dónde tengo bicis disponibles cerca?</em>” a un asistente con interfaz conversacional. Al final son los tipos de respuestas que intentamos responder ya <a href="https://danilat.com/weblog/2010/04/19/nuestra-aportacion-en-el-desafio-abredatos-2010-dndzgz-com">en tiempos del AbreDatos 2010</a> con una interfaz pensada para usarse en un teléfono móvil.</p>

<p>Con esta idea tiré por lo ya conocido, el <a href="https://developers.google.com/maps/documentation/geocoding/overview">API de Google Maps para geocoding</a>, aunque podría haber tirado también por cualquier otro proveedor. Y exponerlo como otra tool, intentando forzar que siempre busque concatenando al valor recibido <em>Zaragoza, Spain</em>.</p>

<p>Tras eso las respuestas eran ya prometedoras, ya que parece que estos modelos trabajan bastante bien con posiciones geolocalizadas. Por ejemplo preguntándole sobre paradas de bicis cerca de la Avenida Madrid decía esto.</p>

<p><img src="/img/posts/2025/mcp2/5-avda-madrid.png" alt="Captura de Claude Desktop devolviendo paradas de Bizi por Avenida Madrid" /></p>

<p>Todavía en este momento se devolvían las más de 100 estaciones de bizi que hay disponibles, y en el caso de los buses eran más de 1000. Lo siguiente era pasar la posición dada a partir de una dirección a las tools que traen las ubicaciones con sus respectivas posiciones, comprobar la distancia entre posiciones usando una implementación de la <a href="https://es.wikipedia.org/wiki/F%C3%B3rmula_del_semiverseno">fórmula de Harversine</a>, ordenarlas por cercanía y devolver un top razonable para que los LLMs hagan lo suyo.</p>

<p>Una vez implementado eso, dejando el límite a 10, se acotan mucho los resultados y la experiencia aparentemente era algo mejor. Ahora ordenando por distancia entre los puntos e informa sobre ello, además se ofrecen otras opciones indicando lo lejos del punto de referencia.</p>

<p><img src="/img/posts/2025/mcp2/6-compromiso-caspe.png" alt="Captura de Claude Desktop devolviendo paradas de Bizi por Compromiso de Caspe" /></p>

<p>A partir de ahí el problema del tamaño del bus dejó de serlo.</p>

<p><img src="/img/posts/2025/mcp2/7-bus-pza-aragon.png" alt="Captura de Claude Desktop responidendo sobre los tiempos de la línea 22 en plaza Aragón dirección a Las Fuentes" /></p>

<p>Durante mis pruebas me encontré que Claude intenta llamar varias veces a las tools en ocasiones donde considera que las distancias son alejadas o cuando no consigue obtener datos del estado en tiempo real, está claro que no le gusta quedar mal 🙂. Por ejemplo con esta prueba preguntando sobre una línea de bus en una dirección.</p>

<p><img src="/img/posts/2025/mcp2/8-30-san-miguel.png" alt="Captura de Claude Desktop tratando de responder con estimaciones del 30 en Plaza San Miguel intentando corregir errores del API" /></p>

<p>También para variar y ver más comportamientos, hice alguna prueba con GitHub Copilot y GPT-4o en modo agente, con los que la experiencia a priori me resultó bastante similar.</p>

<p><img src="/img/posts/2025/mcp2/9-bus-bizi.png" alt="Captura de GitHub Copilot preguntando sobre el Circular 2 en camino de las Torres" /></p>

<p>Hay otras tools que podrían llegar a ser muy útiles y serían complementarias, por ejemplo para cubrir casuísticas de recomendar de cómo <strong>llegar de un punto a otro de la ciudad en transporte público</strong>. Ya que los datos con los que fueran entrenados los modelos pueden haber quedado desactualizados o puede que se los estén inventando.</p>

<p>Por ejemplo, al preguntarle cómo llegar de Plaza Aragón a Arcosur en tranvía me decía correctamente que a Arcosur no llega el tranvía y que tendría que hacer transbordo tras la última parada. Pero me ofrecía como alternativa sin trasbordo la posibilidad de la línea de bus 59 desde Plaza de España, al pedirle las estimaciones <strong>terminó dándose cuenta que esa opción no existe</strong> por sí mismo.</p>

<p><img src="/img/posts/2025/mcp2/10-59-centro-arcosur.png" alt="Captura de Claude Desktop equivocándose con la línea 59 y posteriormente corrigiéndose a sí mismo" /></p>

<h3 id="conclusiones">Conclusiones</h3>

<p>Este experimento con tools de MCP para darles capacidades extra a los LLMs me ha resultado muy entretenido. En esta evolución he intentado darle un enfoque de solución un poco más <em>de producto</em> y no quedarme meramente en probar el protocolo.</p>

<p>He buscado soluciones pensando en las personas usuarias, aunque he hecho 0 investigación, sí he hecho memoria de feedback y críticas recibidas sobre las aplicaciones móviles de DNDzgz durante estos años.</p>

<p>Algunos pensamientos al respecto de este side-project:</p>

<ul>
  <li>Las tools mucho mejor si envían respuestas ligeras, por cuestiones de eficiencia de red, coste, consumo energético, tiempos de respuesta, etc.</li>
  <li>Tratar de dar buenas descripciones a las tools para facilitar que los LLMs tengan más claro cuando usarlo respecto a las intenciones de las personas usuarias.</li>
  <li><em>Eat your own dog food</em> como para cualquier side-project, salvo que sólo quieras cacharrear con la tecnología y luego olvidarte.</li>
  <li>Que alguien más lo pruebe te va a ayudar a mejorarlo, aunque no hagas test con personas usuarias al uso viene bien tener otros puntos de vista.</li>
  <li>Al usar directamente estos LLMs con estos clientes tratan de quedar bien, a veces eso significa que puedan alucinar. Así que lo suyo es facilitarles tools que puedan ayudarles a no hacerlo en los temas que nos competen.</li>
  <li>Como se van a buscar la vida para dar una respuesta, esto significa que pueden hacer muchas llamadas a su aire a nuestras tools sin que la persona que le ha pedido algo intervenga.</li>
  <li>Por esto último, ahora mismo me daría un poco de miedo el exponer acciones destructivas en una tool que no se puedan deshacer: Borrar documentos, sobreescribir información sin versionado, etc.</li>
  <li>Supongo que montando un chatbot especializado, con prompts de sistema para los LLMs y que actúe como cliente MCP, son potenciales problemas que se pueden solventar. A corto plazo dudo que me meta en ese fregao 😀.</li>
</ul>

<p>Podéis ver el código de <a href="https://github.com/danilat/mcp-dndzgz">mcp-dndzgz en github</a> con los cambios comentados en el artículo.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Segunda parte de jugar con MCP. Tras añadir el soporte a los servicios de bus y bizi, descubrí que posiblemente podía mejorar la experiencia de usuario y hacer un uso más eficiente de los LLMs usando capacidades de geolocalización.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://danilat.com/img/posts/2025/mcp2/1-bizi.png" /><media:content medium="image" url="https://danilat.com/img/posts/2025/mcp2/1-bizi.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Jugando con MCP protocol. Introducción.</title><link href="https://danilat.com/weblog/2025/04/21/jugando-mcp-dndzgz" rel="alternate" type="text/html" title="Jugando con MCP protocol. Introducción." /><published>2025-04-21T00:00:00+00:00</published><updated>2025-04-21T00:00:00+00:00</updated><id>https://danilat.com/weblog/2025/04/21/jugando-mcp-dndzgz</id><content type="html" xml:base="https://danilat.com/weblog/2025/04/21/jugando-mcp-dndzgz"><![CDATA[<p>Llevo unas semanas leyendo un poco sobre el hype de <a href="https://modelcontextprotocol.io/introduction"><strong>Model Context Protocol</strong></a>, hay mucho escrito ya sobre MCP y en mi caso sólo me he asomado a este protocolo muy tímidamente, pero comparto algunas referencias que me han parecido muy interesantes:</p>

<ul>
  <li><a href="https://modelcontextprotocol.io/docs/concepts/architecture">La documentación oficial</a>. Hosts, Clients, Servers, Stdio/Http con SSE y el uso de JSON-RPC en la transport layer, etc.</li>
  <li><a href="https://blog.sshh.io/p/everything-wrong-with-mcp">Everything Wrong with MCP</a>. Shrivu Shankar escribe sobre problemas y limitaciones relacionados con seguridad y la experiencia de uso.</li>
  <li><a href="https://docs.mcp.run/blog/2025/03/27/mcp-differential-for-modern-apis">MCP: The Differential for Modern APIs and Systems</a>. Steve Manuel escribe como MCP puede ayudar a tener integraciones más resilientes entre sistemas.</li>
</ul>

<p>Mientras he ido leyendo fui pensando en qué pequeño pet-project podía hacer para experimentar un poco y me acordé de <a href="http://www.dndzgz.com/">DNDzgz</a>.</p>

<p>Y aunque el preguntarle a <strong>Claude Desktop o a Cursor</strong> las estimaciones de llegada del tranvía a plaza Aragón para ver si sales ya del estudio o del coworking en un día de cierzo 🥶 a mi tampoco me parece una killer feature. Pensé que podía ser una casuística fácil de implementar pedirle los tiempos de llegada del tranvía, que son datos que se requiere tener en tiempo real.</p>

<p><img src="/img/posts/2025/mcp/screenshot.png" alt="En la captura se pregunta ¿Cuándo llegará el tranvía a la parada de Romareda? y luego se ve la respuesta de Claude" title="Captura de pantalla de Claude Desktop usando MCP DNDzgz" /></p>

<p>Técnicamente no tiene mucho misterio:</p>
<ul>
  <li>El MCP server está implementado con <a href="https://modelcontextprotocol.io/docs/concepts/transports#type-script-server">Node y el transport Standard Input/Output</a>, así que los MCP Hosts se encargan de arrancarlo.</li>
  <li>Se hacen llamadas fetch al API de DNDzgz, a los endpoints que devuelven todas las paradas del tranvía y el tiempo estimado en cada parada.</li>
  <li>Y esto se expone como dos diferentes <a href="https://modelcontextprotocol.io/docs/concepts/tools">Tools</a> para que los modelos lo llamen tras que la persona que lo usa de el ok.</li>
</ul>

<p>Podéis ver el código de <a href="https://github.com/danilat/mcp-dndzgz">mcp-dndzgz en github</a>.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Llevo unas semanas leyendo un poco sobre el hype de Model Context Protocol, hay mucho escrito ya sobre MCP y en mi caso sólo me he asomado a este protocolo muy tímidamente, pero comparto algunas referencias que me han parecido muy interesantes:]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://danilat.com/img/posts/2025/mcp/screenshot.png" /><media:content medium="image" url="https://danilat.com/img/posts/2025/mcp/screenshot.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Desarrollo de producto en una Scale-Up. SCPNA 2024</title><link href="https://danilat.com/weblog/2025/03/09/scpna-desarrollo-producto-scaleup" rel="alternate" type="text/html" title="Desarrollo de producto en una Scale-Up. SCPNA 2024" /><published>2025-03-09T00:00:00+00:00</published><updated>2025-03-09T00:00:00+00:00</updated><id>https://danilat.com/weblog/2025/03/09/scpna-desarrollo-producto-scaleup</id><content type="html" xml:base="https://danilat.com/weblog/2025/03/09/scpna-desarrollo-producto-scaleup"><![CDATA[<p>Más vale tarde que nunca, recopilo y comparto por aquí la charla que preparé para la <a href="https://pamplonaswcraft.com/">Software Crafters Pamplona 2024</a>: <strong>Desarrollo de producto en una Scale-Up</strong></p>

<blockquote>
  <p>Sígueme en esta historia donde te contaré cómo pasamos en Genially de una idea, un insight a una feature 100% desplegada en producción con su Go to Market incluido.
Vamos a ver todo el value-stream por el cual pasamos, los actores, viendo cómo nos organizamos, procesos que seguimos, el tooling que usamos para las diferentes fases y algunas curiosidades más. Seguro que pasamos un rato entretenido.</p>
</blockquote>

<p>Esta charla la tenía apalabrada <a href="https://www.linkedin.com/in/chemaroldan/">Chema Roldán</a>, CTO de <a href="https://genially.com/">Genially</a>, con la buena gente de Software Crafters Pamplona. Pero como finalmente tenía problemas de agenda terminé preparándola yo con su ayuda y la de algunas otras personas de la compañía.</p>

<p><img src="/img/posts/2025/scpna-2024/conference.png" alt="Foto de una de las salas del evento" title="En la foto se ven cabezas de varias personas sentadas de espaldas y entre medio al fondo yo de pie en el escenario con la presentación en el proyector del fondo" /></p>

<p>Después de darle varias vueltas la terminamos estructurando en 4 partes:</p>
<ul>
  <li><strong>Organización</strong>: Dando contexto de la compañía, número de personas y qué equipos había en ese momento, la planificación de iniciativas por trimestre, prácticas de coordinción y herramientas que utilizamos.</li>
  <li><strong>Descubrimiento</strong>: Para explicar cómo típicamente se idean y da forma a las iniciativas, la manera en la que se aplica el modelo de diseño de <em>doble diamante</em>, etc.</li>
  <li><strong>Entrega</strong>: La parte técnica de cómo se construye y despliega el software, introduciendo algunos retos de trabajar con un monorepo, la branching strategy utilizada, la frecuencia de despliegue, etc. Hablando de las herramientas técnicas que se venían usando.</li>
  <li><strong>Go to Market</strong>: Donde se entraba en detalle de las estrategias de rollout de funcionalidades, cómo se comunicaba internamente al resto de la compañía y externamente a nuestros clientes, el stack de observabilidad y telemetría tanto técnico como de producto y cómo se gestionan los incidentes.</li>
</ul>

<p>Muchos meses más tarde como parte de la cultura de mejora continua algunas cosas se han ido iterando y puliendo, pero el grueso de la charla sigue siendo válida a día de hoy.</p>

<p>Aquí dejo el vídeo</p>
<iframe width="100%" height="450" src="https://www.youtube.com/embed/8NHZZcZUcBM" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p>Y por aquí el genially de la presentación</p>
<div style="width: 100%;"><div style="position: relative; padding-bottom: 56.25%; padding-top: 0; height: 0;"><iframe title="Desarrollo de producto en una scale-up. Desde la idea al mercado
" frameborder="0" width="560" height="315" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" src="https://view.genially.com/662a4c0290532c0014815f3a" type="text/html" allowscriptaccess="always" allowfullscreen="true" scrolling="yes" allownetworking="all"></iframe> </div> </div>

<p>¡Nos vemos en la edición de 2025! Esta vez como asistente, sin la presión de tener que presentar :).</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Más vale tarde que nunca, recopilo y comparto por aquí la charla que preparé para la Software Crafters Pamplona 2024: Desarrollo de producto en una Scale-Up]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://danilat.com/img/posts/2025/scpna-2024/conference.png" /><media:content medium="image" url="https://danilat.com/img/posts/2025/scpna-2024/conference.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Equipos multidisciplinares de producto en Mosaic</title><link href="https://danilat.com/weblog/2025/02/20/escribiendo-equipos-multidisciplinares-producto-digital-mosaic" rel="alternate" type="text/html" title="Equipos multidisciplinares de producto en Mosaic" /><published>2025-02-20T00:00:00+00:00</published><updated>2025-02-20T00:00:00+00:00</updated><id>https://danilat.com/weblog/2025/02/20/escribiendo-equipos-multidisciplinares-producto-digital-mosaic</id><content type="html" xml:base="https://danilat.com/weblog/2025/02/20/escribiendo-equipos-multidisciplinares-producto-digital-mosaic"><![CDATA[<p>Desde hace bastante tiempo que de vez en cuando algunas personas me habían ido dejando caer lo de que me animara a publicar algo o preparar alguna charla relacionada en <strong>cómo colaborar mejor entre diseño y desarrollo</strong> en esto del desarrollo de software o producto digital. Para charla la verdad que nunca me había motivado mucho, pero en un par de ocasiones arranqué borradores con enfoques diferentes, pero al final los terminé dejando aparcados por no terminar de encontrar un hilo conductor.</p>

<p>Pero cuando hace unos meses me llegó la propuesta de <a href="https://bsky.app/profile/cesar.corcoles.net">César</a> de escribir sobre equipos multidisciplinares de producto, con foco principalmente en quiénes tienen perfiles de desarrollo y diseño, para la revista <a href="https://mosaic.uoc.edu/"><strong>Mosaic</strong></a> de la <a href="https://www.uoc.edu/">Universitat Oberta de Cataluña</a>, me decidí a hacer un nuevo intento.</p>

<p><a href="https://mosaic.uoc.edu/2025/01/16/equipos-multidisciplinares-de-desarrollo-de-producto-digital/"><img src="/img/posts/2025/mosaic/mosaic.jpg" alt="Esquema representando un flujo desde descubrimiento a despliegue y aprendizaje" title="Esquema representando un flujo desde descubrimiento a despliegue y aprendizaje" /></a></p>

<p>Para mi era un reto combinar el darle un enfoque algo generalista, dado la variedad de audiencia que tiene la revista, con intentar que aportase algo a profesionales del sector que lleguen a leerlo. Por eso evité usar demasiados tecnicismos más allá de algunas referencias relacionadas con Lean Software Development, Design Systems/Tokens y Hot Potato Process que me servían como puntos de apoyo para desarrollar el artículo y llegar a la conclusión final:</p>

<blockquote>
  <p>“En el mundo de creación de proyectos o productos digitales, los equipos multidisciplinares van a seguir siendo tendencia por su mejor adaptación al actual entorno cambiante. Para que estos funcionen bien, debemos tratar de trabajarlos a nivel personal, con el resto del equipo y a nivel organizacional.</p>
</blockquote>

<blockquote>
  <p>Y, en esencia, en cualquiera de los niveles, debemos buscar tres factores:</p>
  <ul>
    <li>Tratar de que las personas que formen el equipo estén alineadas hacia una meta común. Así como que su meta esté dentro de los objetivos de la organización.</li>
    <li>Tratar de que el conjunto de capacidades individuales haga viable alcanzar esa meta, o que al menos puedan llegar a capacitarse para hacerlo.</li>
    <li>Tratar de que siempre haya respeto, y de conseguir que exista empatía entre las personas del equipo y del resto de la organización con las que se interactúe.</li>
  </ul>
</blockquote>

<blockquote>
  <p>Sí, factores infinitamente más fáciles de verbalizar que de conseguir.”</p>
</blockquote>

<p>Lo puedes leer en castellano: <a href="https://mosaic.uoc.edu/2025/01/16/equipos-multidisciplinares-de-desarrollo-de-producto-digital/"><strong>Equipos multidisciplinares de desarrollo de producto digital</strong></a></p>

<p>També ho pots llegir en catalá: <a href="https://mosaic.uoc.edu/2025/01/16/equips-multidisciplinaris-de-desenvolupament-de-producte-digital/"><strong>Equips multidisciplinaris de desenvolupament de producte digital</strong></a></p>

<p>And also in english: <a href="https://mosaic.uoc.edu/2025/01/16/multidisciplinary-digital-product-development-teams/"><strong>Multidisciplinary digital product development teams</strong></a></p>]]></content><author><name></name></author><summary type="html"><![CDATA[Hace unos meses me llegó la propuesta de escribir en la revista Mosaic de la Universitat Oberta de Cataluña sobre equipos multidisciplinares de producto]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://danilat.com/img/posts/2025/mosaic/mosaic.jpg" /><media:content medium="image" url="https://danilat.com/img/posts/2025/mosaic/mosaic.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">El lado estratégico de Domain-Driven Design. CommitConf 2024</title><link href="https://danilat.com/weblog/2024/05/08/commitconf" rel="alternate" type="text/html" title="El lado estratégico de Domain-Driven Design. CommitConf 2024" /><published>2024-05-08T00:00:00+00:00</published><updated>2024-05-08T00:00:00+00:00</updated><id>https://danilat.com/weblog/2024/05/08/commitconf</id><content type="html" xml:base="https://danilat.com/weblog/2024/05/08/commitconf"><![CDATA[<p>Hace unas semanas estuve en <a href="https://2024.commit-conf.com/">Commit Conf en Madrid</a>, evento al que no iba desde hace mucho. Estuve compartiendo la charla <em>El lado estratégico de Domain-Driven Design</em>, iterando ligeramente la que hice hace unos meses en <a href="https://www.linkedin.com/company/la-vertical/">La Vertical</a>, a su vez basada en una charla más larga (y espesa) que he impartido in-company en varias ocasiones.</p>

<p><img src="/img/posts/2024/commitconf/conference.jpg" alt="Foto de la sala de conferencias del track 1 del evento" title="En la foto se ven cabezas de la gente viéndolo y al fondo yo de pie en el escenario con la presentación en una pantalla grande" /></p>

<p>En los últimos años ha crecido el interés y la adopción de Domain-Driven Design en la comunidad de desarrollo de software. Ahora es bastante habitual oír hablar del <strong>lado táctico de DDD</strong>, la mayoría de las veces acompañado del uso de ports &amp; adapters (aka Hexagonal Architecture). Pero, al menos en castellano, no he visto hablar apenas de <strong>DDD estratégico</strong> y lo que nos puede aportar a nivel de <em>Sociotechnical Architecture</em>.</p>

<p>Así que de ahí vino buena parte de mi motivación de proponer repetirla en un evento mucho más masivo como es la Commit Conf.</p>

<p>La presentación está dividida en 4 bloques:</p>
<ul>
  <li>Introducción a DDD y específicamente a la parte estratégica</li>
  <li>Resumen de las actividades estratégicas basado en el <a href="https://github.com/ddd-crew/ddd-starter-modelling-process">Domain-Driven Design Starter Modelling Process de DDD Crew</a></li>
  <li>Un ejemplo práctico de DDD estratégico basado en un caso real, mostrando su división y conexión entre dominios visibilizándolo con un <strong>Context Map</strong> un tanto enriquecido con su clasificación desde diferentes puntos de vista</li>
  <li>Otras consideraciones a tener a nivel de la organización de equipos</li>
</ul>

<p>Aquí os dejo el vídeo de la charla</p>
<iframe width="100%" height="450" src="https://www.youtube.com/embed/IlbgE_g5JJQ" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p>Y el genially que usé para la presentación</p>
<div style="width: 100%;"><div style="position: relative; padding-bottom: 56.25%; padding-top: 0; height: 0;"><iframe title="Commit Conf - El lado estratégico de DDD" frameborder="0" width="560" height="315" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" src="https://view.genial.ly/65fc6aefab428e0014eadbf8" type="text/html" allowscriptaccess="always" allowfullscreen="true" scrolling="yes" allownetworking="all"></iframe> </div> </div>]]></content><author><name></name></author><summary type="html"><![CDATA[Hace unas semanas estuve en Commit Conf en Madrid, evento al que no iba desde hace 6 años. Estuve compartiendo la charla El lado estratégico de Domain-Driven Design]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://danilat.com/img/posts/2024/commitconf/conference.jpg" /><media:content medium="image" url="https://danilat.com/img/posts/2024/commitconf/conference.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Reduciendo riesgos con tests de carga</title><link href="https://danilat.com/weblog/2024/04/04/tests-de-carga" rel="alternate" type="text/html" title="Reduciendo riesgos con tests de carga" /><published>2024-04-04T00:00:00+00:00</published><updated>2024-04-04T00:00:00+00:00</updated><id>https://danilat.com/weblog/2024/04/04/tests-de-carga</id><content type="html" xml:base="https://danilat.com/weblog/2024/04/04/tests-de-carga"><![CDATA[<p>Hace varias semanas estuve involucrado en realizar algunos tests de carga en Genially, algo que no había tenido necesidad de hacer desde que trabajé en Inditex lanzando un nuevo servicio interno.</p>

<p>Esto venía dado por unos cambios en los que estuvimos trabajando un par de equipos para mejorar la experiencia de uso de una parte del producto, lo cual implicó un cambio bastante importante a nivel de arquitectura.</p>

<p>Con estos cambios teníamos 2 riesgos:</p>
<ul>
  <li>Que aunque la experiencia de uso de la funcionalidad mejorase esto pudiera impactar negativamente en un funnel de conversión.</li>
  <li>Que la nueva solución que habíamos implementado pudiera causar problemas dependiendo de la carga y tuviéramos incidencias.</li>
</ul>

<p>El primer riesgo lo minimizamos realizando un rollout incremental, que es como lanzamos la mayoría de cambios relevantes en Genially. Esto, en este caso, significó lanzar los cambios internamente bajo una feature flag para obtener feedback cualitativo y luego abrirlo a un porcentaje del tráfico para observar las métricas de producto.</p>

<p>El segundo riesgo, como mencionaba al principio, lo minimizamos realizando algunos <strong>tests de carga</strong>.</p>

<h3 id="pero-qué-es-un-test-de-carga">¿Pero qué es un test de carga?</h3>

<p>Es un tipo de prueba en la que se <strong>genera tráfico de forma artificial para evaluar la respuesta o capacidad de un sistema</strong> ante una carga determinada de trabajo o de personas usuarias, por lo que puede servir para comprobar tanto el rendimiento como el escalado de un sistema.</p>

<p>Para esto, antes de ejecutar la prueba, necesitamos tener definido previamente qué y cómo lo vamos a observar para poder consultarlo tras su ejecución (tiempos de respuesta, consumo de recursos, etc). Así que <strong>el entorno sobre el que vayamos a probar tiene que ser observable</strong>; en este caso, lo que más nos va a interesar son las métricas y, en caso de que empiece a degradarse el servicio, también las trazas pueden ayudar a identificar el origen del problema con mayor facilidad.</p>

<p>En ocasiones, este tipo de pruebas se tienden a hacer con personas reales de manera algo informal, en plan “entrad aquí X personas a hacer Y metiéndole caña y vamos a ver cómo van las métricas Z”. Eso puede ser perfectamente válido para tener una idea general de cómo responde el sistema con una carga un tanto aleatoria, pero tiene el problema de que no es repetible ni controlado, por lo que de ese modo no podemos dar seguimiento a los resultados obtenidos de forma consistente.</p>

<p>Para tener consistencia en este tipo de pruebas, hay herramientas que nos permiten automatizarlas, de ese modo obtenemos escenarios controlados y repetibles a los que sí podemos dar seguimiento. Con estas herramientas, podremos definir distintos escenarios en los que queremos probar el sistema y observar si se mejora o empeora comparando los resultados de antes y después de un cambio.</p>

<p>En cuanto a herramientas concretas, en el pasado usé <a href="https://httpd.apache.org/docs/2.4/programs/ab.html">Apache HTTP Server Benchmarking Tool</a> y <a href="https://jmeter.apache.org/">JMeter</a>, pero en la última ocasión lo hice con <a href="https://k6.io/">k6</a> por recomendación de mi compañero <a href="https://twitter.com/ManuFS95">Manu Franco</a>. La verdad es que me pareció una herramienta fácil de empezar a usar, y viendo su documentación también muy potente, así que de momento se ha convertido en mi preferencia.</p>

<h3 id="tipos-de-tests-de-carga">Tipos de tests de carga</h3>

<p>Dentro de los tests de carga, se pueden clasificar en subtipologías dependiendo del objetivo de la prueba y del patrón de generación de tráfico utilizado. Me gusta mucho <a href="https://grafana.com/load-testing/types-of-load-testing/">la gráfica y la explicación de la propia documentación de k6</a>.</p>

<p><a href="https://grafana.com/load-testing/types-of-load-testing/"><img src="/img/posts/loadtests/types.png" alt="Esquema representando los distintos tipos de tests" title="Esquema representando los distintos tipos de tests, muestra su relación volumen de carga/tiempo" /></a></p>

<ul>
  <li>Smoke tests: son pruebas sobre el sistema de corta duración (segundos o pocos minutos) con una carga baja, con el objetivo de comprobar que <strong>todo funciona razonablemente bien sin consumir muchos recursos</strong>. De primeras, no los hubiera incluido como test de carga, pero dada la aproximación de esta herramienta de generar tráfico concurrente, les compro el incluirlo. En este caso, se podrían lanzar de forma bastante recurrente para detectar errores de configuración a nivel de aplicación o anomalías en las métricas de forma temprana.</li>
  <li>Average-load test: son pruebas sobre el sistema de duración media (minutos-hora) con una carga similar a la habitual, con el objetivo de <strong>asegurar que los cambios introducidos no impactan negativamente en el contexto habitual del sistema</strong>. Esto podría hacerse de forma periódica para encontrar potenciales problemas que se hayan podido introducir.</li>
  <li>Stress test: son pruebas sobre el sistema de duración media (minutos-hora) con una carga por encima de la habitual, con el objetivo de <strong>comprobar el comportamiento del sistema con un tráfico bastante superior al habitual</strong>. Esto nos puede ser útil, por ejemplo, para prepararnos para campañas como navidad o rebajas en el mundo del comercio electrónico.</li>
  <li>Spike test: de duración corta (unos pocos minutos) con una carga que sobrepase mucho la habitual del sistema. Su objetivo es ver <strong>cómo se comporta con un pico de tráfico masivo durante un tiempo más limitado</strong>. Escenarios para los que esto puede ser útil pueden ser prepararse para las primeras horas del Black Friday, si se va a lanzar un anuncio en prime time en televisión, etc.</li>
  <li>Breakpoint test: de duración indeterminada y una carga incremental hasta llegar a que el sistema se rompa o llegue al límite que hayamos definido. En este caso, el objetivo es <strong>llevar el sistema al extremo máximo para conocer en qué momento nuestro sistema no da más de sí o hasta dónde permitimos escalarlo</strong> si la infraestructura del sistema puede ir hacia “infinito”. Los escenarios podrían ser comprobar optimizaciones de partes del sistema o trabajar en un plan de contingencia si en algún momento el sistema se acerca a su límite.</li>
  <li>Soak tests: de larga duración (varias horas) y una carga similar a la habitual. Su objetivo es <strong>detectar problemas surgidos a partir de un uso extendido del sistema</strong>, como el aumento del consumo de infraestructura o la degradación de los tiempos de respuesta. Esto nos puede interesar especialmente cuando no somos los dueños de la infraestructura en la que corre nuestro sistema y queramos comprobar que quienes lo vayan a operar no se encuentren sorpresas posteriormente.</li>
</ul>

<h3 id="cómo-lanzar-los-tests-de-carga">¿Cómo lanzar los tests de carga?</h3>

<p>En un mundo ideal lo probaríamos en algún entorno aislado que se asemeje mucho a producción a nivel de infraestructura, pero no siempre podremos contar con esa posibilidad. Y la frencuencia de ejecución dependerá de cada contexto.</p>

<p>Por ejemplo, cuando trabajaba en Inditex, disponíamos de un entorno específico para este tipo de pruebas. Y dado que no era posible realizar llamadas entre entornos distintos debido a que estaba limitado a nivel de red, sabíamos que podíamos probar nuestros servicios de forma aislada sin necesidad de coordinarnos con equipos no involucrados en estas pruebas.</p>

<p>Por otro lado, para llevar a cabo pruebas preliminares del cambio de arquitectura al que me refería en Genially, las estuvimos realizando en un <a href="https://tech.genial.ly/environment-as-a-service-f7966d609744">entorno efímero</a>. A nivel de infraestructura, estos entornos efímeros son bastante limitados en comparación con el de producción, pero nos permitía realizar algunas validaciones en un entorno aislado también sin necesidad de coordinación. Utilizamos este entorno para ejecutar una serie de smoke tests y un mini average-load test para obtener las métricas base. Luego introdujimos los cambios relevantes y comprobamos si surgía alguna anomalía para ver si había que iterar algo, una vez visto que no había nada raro podíamos ir a producción con mayor confianza y darle seguimiento al uso real de las primeras horas.</p>

<p>En los casos que describo lanzábamos las pruebas de forma manual y luego analizábamos los resultados. Pero también existen contextos donde estas pruebas se lanzan automáticamente incluso en <strong>pipelines de continuous delivery</strong>. Así que se puede echar para atrás una release si un test falla dado el límite marcado como aceptable en una métrica. Por ejemplo si dada una carga se supera el máximo de latencia de peticiones, no se consigue ingestar un mínimo de peticiones por segundo, etc.</p>

<h3 id="concluyendo">Concluyendo</h3>

<p>Hay lugares donde este tipo de pruebas son muy relevantes por su contexto y forman parte del camino de entrega del software. En mi caso no han formado nunca parte de mi flujo habitual de trabajo, pero han habido ocasiones en las que me han resultado muy útiles para lanzar nuevos servicios, nuevas funcionalidades o para introducir cambios relevantes en la arquitectura <strong>con una mayor confianza</strong>.</p>

<p>Aunque nunca hay que olvidar que, como cualquier prueba automática, estas pruebas pueden ayudar a minimizar el riesgo pero <strong>no garantizan la ausencia total de problemas de degradación o errores</strong>. Ya que el tráfico artificial nunca será igual al generado a partir del comportamiento real de las personas que utilizan nuestro software, así que es importante <strong>invertir primero en observabilidad</strong> y en comprender cómo se comportan nuestros sistemas de software en producción.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Hace varias semanas estuve involucrado en realizar algunos tests de carga en Genially, algo que no había tenido necesidad de hacer desde que trabajé en Inditex lanzando un nuevo servicio interno.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://danilat.com/img/posts/loadtests/types.png" /><media:content medium="image" url="https://danilat.com/img/posts/loadtests/types.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Buscando el Product Market Fit y Arquitectura Hexagonal</title><link href="https://danilat.com/weblog/2024/01/23/hexagonal-product-market-fit" rel="alternate" type="text/html" title="Buscando el Product Market Fit y Arquitectura Hexagonal" /><published>2024-01-23T00:00:00+00:00</published><updated>2024-01-23T00:00:00+00:00</updated><id>https://danilat.com/weblog/2024/01/23/hexagonal-product-market-fit</id><content type="html" xml:base="https://danilat.com/weblog/2024/01/23/hexagonal-product-market-fit"><![CDATA[<p>Hace un par de meses estuve en <a href="https://www.linkedin.com/company/la-vertical/">La Vertical by Mercadona Tech</a> hablando sobre DDD estratégico. Al final del evento, pude conocer a algunas personas con quienes estuve compartiendo impresiones, ideas, proyectos…</p>

<p>En un momento dado, una persona me preguntó sobre qué opinaba de usar el estilo de arquitectura de Ports &amp; Adapters, más popularmente conocida como <a href="https://alistair.cockburn.us/hexagonal-architecture/">Arquitectura Hexagonal</a>, cuando el contexto es la <strong>fase de búsqueda o consolidación del Product Market Fit</strong>. Me pilló la pregunta algo a contrapié y creo que le respondí de forma un poco tibia; podríamos resumirlo con un clásico: <em>“No lo sé, creo que <strong>depende del equipo y del contexto</strong>“</em>.</p>

<p>Esto es algo sobre lo que he compartido mi punto de vista en petit comité en bastantes ocasiones, así que quería extender un poco esa respuesta aterrizándolo en este artículo.</p>

<p><img src="/img/posts/pmf-arquitectura-hexagonal/draw.png" alt="Dibujo que representa el product market fin, un hexágono dividido de driver y driven con un interrogante encima" title="Dibujo que representa el product market fin, un hexágono dividido de driver y driven con un interrogante encima" /></p>

<h3 id="product-market-fit">¿Product Market Fit?</h3>

<p>Este es el momento en el que un producto no está validado a nivel de negocio, o al menos no totalmente. Son momentos donde aún no es rentable y hay que probar a lanzar soluciones para cubrir los problemas de nuestros potenciales clientes e ir iterando (o descartando) esas soluciones. Más info sobre esto en este artículo de la lista de correo de <a href="https://disaaster.io/que-es-el-product-market-fit/">Ignacio Arriaga: ¿Qué es el product market fit y cómo acelerarlo?</a>.</p>

<p>Así que en una situación en la que no hemos consolidado el Product Market Fit, la capacidad de iterar un producto de software a una velocidad razonablemente alta es crítica para hacerlo. Más en situaciones en las que tenemos un presupuesto limitado, como suele ser el de la mayoría de startups.</p>

<h3 id="arquitectura-hexagonal">¿Arquitectura Hexagonal?</h3>

<p>Si seguimos el estilo de Hexagonal Architecture o Ports &amp; Adapters o Clean Architecture, colocamos en el medio nuestra lógica de negocio y nos abstraemos de los detalles de implementación de infraestructura.</p>

<p>Esta infraestructura son tanto los <em>driver ports</em> que son el mecanismo de entrega para interactuar con la lógica de negocio, como podrían ser server-side rendering, REST, gRPC, GraphQL, CLI, tareas programadas… Como los <em>driven ports</em> que son los encargados de mantener el estado vía persistencia de datos y la comunicación hacia otros sistemas.</p>

<p>Las características principales de este estilo de arquitectura son la cambiabilidad de la infraestructura y, como consecuencia de eso, la testeabilidad. Ya que al utilizar inyección de dependencias nos abstraemos de las diferentes piezas de infraestructura y podemos testear la lógica de negocio de forma unitaria.</p>

<p>Frente a lo que me parece percibir a veces, no hay una forma única de implementar software con este estilo de arquitectura. En mi caso, ha evolucionado un poco mi forma de trabajar con este enfoque, pero como base siguen siendo bastante válidos los contenidos que compartimos en <a href="https://medium.com/coding-stones/clean-architecture-tour-2016-2017-90bb205d443c">algunas charlas de hace algunos años con Coding Stones</a>.</p>

<h4 id="lo-malo">Lo malo</h4>

<p>Este estilo de arquitectura se ha popularizado mucho en los últimos tiempos y ha crecido de la mano con el uso conjunto de los <a href="https://danilat.com/weblog/2019/09/17/ddd-in-a-shot">patrones tácticos de DDD</a>. Tanto que para muchas personas creo que no hay diferencia, no se percibe que son cosas distintas que unas veces se complementan y otras no tiene demasiado sentido aplicar, al menos no de forma <em>purista</em>.</p>

<p>Así que esto en ocasiones lleva a sobreingeniería en el diseño, en forma de exceso de abstracciones y aumento de complejidad extrínseca, haciendo un código más difícil de modificar y extender funcionalmente. Lo que provoca que en esos casos de aplicaciones con una lógica de dominio no demasiado compleja, se ralentice la velocidad de iteración en el producto.</p>

<p>También, otras veces, se espera que al aplicar este estilo de arquitectura desaparezcan todos los problemas asociados a la deuda técnica. Como si ya vineran integrados mágicamente en ello principios como <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a>, <a href="https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it">YAGNI</a>, <a href="https://en.wikipedia.org/wiki/KISS_principle">KISS</a>, <a href="https://en.wikipedia.org/wiki/Separation_of_concerns">Separation of concerns</a>, <a href="https://en.wikipedia.org/wiki/Law_of_Demeter">ley de Demeter</a>, <a href="https://martinfowler.com/bliki/BeckDesignRules.html">las 4 reglas del diseño simple</a>… y nos fuera a evitar tener <a href="https://refactoring.guru/es/refactoring/smells">code smells</a>.</p>

<h4 id="lo-bueno">Lo bueno</h4>

<p>Para mí, lo que aporta este tipo de arquitectura en este tipo de contextos es principalmente la testeabilidad. Cuando se quiere iterar rápido, tener una batería de tests en la que puedas confiar y que se ejecuta rápido es una gran ayuda para mantener el foco en lo que estás desarrollando.</p>

<p>En segundo grado, <a href="https://danilat.com/weblog/2021/12/01/cambio-en-paralelo-event-bus">la cambiabilidad</a> nos aporta que podemos posponer decisiones, por ejemplo, usando soluciones de infraestructura a priori simplistas que nos permitan validar que se aporta valor a pequeña escala, sabiendo que de ser necesario, podemos cambiarla en el futuro con menos esfuerzo al no afectar al diseño.</p>

<h2 id="depende-del-equipo-y-del-contexto">Depende del equipo y del contexto</h2>

<p>Sobre el contexto, empiezo desde una situación en la que se ha llegado a la conclusión de que no podemos servirnos de herramientas no-code ni de un prototipo o prueba de concepto que podamos tirar a la basura dentro de pocos meses. Así que no nos quedan más narices que desarrollar algo que pueda evolucionar, iterarse y adaptarse a lo que nos vayamos encontrando en el futuro.</p>

<p>Normalmente en estos casos seremos un equipo pequeño, con capacidad financiera limitada tanto para contratar como para subcontratar y donde querremos tener buena capacidad para entregar software. Porque, de lo contrario, difícilmente podremos iterar nada.</p>

<p>Cualquier decisión técnica para mí debería ir encaminada a usar soluciones conocidas siempre que sea posible. En general, creo que la línea a seguir es la de <a href="https://mcfunley.com/choose-boring-technology">Choose Boring Technology</a>, con más razón en el caso de estar consolidando el Product Market Fit.</p>

<p>Algunas preguntas que podemos hacernos para pensar sobre esto: ¿Quiénes formamos parte del equipo? ¿Cuánto hemos trabajado usando el estilo de arquitectura de Ports &amp; Adapters? ¿Nuestro software tiene un dominio con cierta complejidad o se parece más a CRUD que encajaría bien acoplado con algún framework? ¿Tenemos algún framework de desarrollo con el que todas las personas del equipo seamos productivas? ¿Sabemos o hemos comprobado si estaremos peleando contra ese framework si lo combinamos con ese estilo de arquitectura? …</p>

<h3 id="alternativas">Alternativas</h3>

<p>En algunas ocasiones, mi aproximación ha sido quedarme un poco a medio camino, utilizando alguno de los artefactos o prácticas que habitualmente se asocian a la Arquitectura Hexagonal, pero manteniendo el acoplamiento al framework en cierto puntos. Esto es: capa de <strong>use cases</strong> para representar lo que hace el producto de software, <strong>inyección de dependencias</strong> para tener piezas cambiables y hacer que la lógica sea más fácil de testear y, si encaja con las necesidades iniciales del producto, también modelar <strong>eventos</strong>.</p>

<h4 id="use-cases">Use Cases</h4>

<p>Esta capa representa las acciones que se pueden hacer sobre el producto de software. A partir de aquí, se encapsula lógica y se orquestan las llamadas a infraestructura. En esta capa no se sabe si va a ser llamada desde un API rest, un comando desde CLI, un cron…</p>

<p>En mi caso, normalmente son clases con constructor y un solo método público para ejecutarlas, así que en un momento dado hasta podrían ser funciones.</p>

<p>En caso de que usemos un ORM (o un ODM o similar), utilizo esas mismas entidades para representar el dominio. Al menos hasta el momento donde se percibe que el modelo de dominio y el modelo de datos entran en conflicto como para tener que hacer una separación.</p>

<h4 id="inyección-de-dependencias">Inyección de dependencias</h4>

<p>En los constructores de los use cases, hago uso intensivo de inyección de dependencias, más para ganar en testeabilidad que en cambiabilidad de la infraestructura. Esto es que en lenguajes de tipado estático, en los constructores normalmente se espera la inyección de una dependencia de una clase y no de una interfaz. El esfuerzo que sí trato de hacer al inyectar esas clases a los use cases, es no acoplarme a nivel de naming de clases y métodos sobre la implementación.</p>

<p>En algunas ocasiones, esta inyección la he mantenido manualmente y otras a través de algún framework de IoC, dependiendo principalmente de si el framework de desarrollo lo trae de serie o si la base de código es aún manejable sin ello.</p>

<h4 id="eventos-opcional">Eventos (opcional)</h4>

<p>En casos donde desde el inicio se observa que existen muchos side-effects en un producto tiendo a introducir eventos de dominio, seguramente no para todos los use cases, pero al menos sí los más relevantes. Pistas para introducirlos pueden ser necesidades relacionadas con: auditoría, distintos tipos de notificaciones, desacoplar la comunicación con otros módulos o sistemas, instrumentar <a href="https://amplitude.com/blog/behavioral-analytics-definition">behavioral analytics</a> en el backend, etc.</p>

<h3 id="conclusiones">Conclusiones</h3>

<p>El mero hecho de usar o no Ports &amp; Adapters no es relevante para que el producto tenga éxito, pero la capacidad de iterar rápido el producto es crítica.</p>

<p>Dentro de que nunca vamos a tener certezas, en momentos donde estamos buscando Product-Market fit a nivel técnico normalmente deberíamos ir hacia <strong>lo más seguro y conocido</strong> por parte del equipo. Bastantes riesgos existen en esos contextos como para asumir más.</p>

<p>Ya sea un estilo de arquitectura completamente acoplado a un framework, de Ports &amp; Adapters o se quede en algún punto intermedio para desarrollar un producto de software, lo que considero innegociable para iterar rápido es acompañarlo de otras 3 prácticas técnicas:</p>
<ul>
  <li>Hacer <strong>testing automático</strong>, al menos unitario y de integración de forma bastante intensiva para tener confianza en los cambios y poder iterar más rápido.</li>
  <li>Tener automatizado el <strong>pipeline de entrega</strong> y que lo pueda hacer cualquiera en el equipo, ya sea pulsando un botón o con un push en el sistema de control de versiones.</li>
  <li>Preparar un mínimo de <strong>telemetría</strong> técnica y de producto, para saber de forma temprana si han surgido problemas en producción a partir de un cambio y poder analizar cómo es el comportamiento de quiénes están usando el producto.</li>
</ul>]]></content><author><name></name></author><summary type="html"><![CDATA[En la búsqueda del Product Market Fit debemos enfocarnos principalmente en la capacidad de iterar las soluciones. Dependiendo del equipo y el contexto a veces la Arquitectura Hexagonal puede ayudarnos, y otras veces no]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://danilat.com/img/posts/pmf-arquitectura-hexagonal/draw.png" /><media:content medium="image" url="https://danilat.com/img/posts/pmf-arquitectura-hexagonal/draw.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Píldora. Precargar datos a bases de datos en Testcontainers</title><link href="https://danilat.com/weblog/2023/06/05/pildora-precargar-bases-datos-testcontainers" rel="alternate" type="text/html" title="Píldora. Precargar datos a bases de datos en Testcontainers" /><published>2023-06-05T00:00:00+00:00</published><updated>2023-06-05T00:00:00+00:00</updated><id>https://danilat.com/weblog/2023/06/05/pildora-precargar-bases-datos-testcontainers</id><content type="html" xml:base="https://danilat.com/weblog/2023/06/05/pildora-precargar-bases-datos-testcontainers"><![CDATA[<p>Una de las cosas en la que ando ayudando últimamente en <a href="https://genially.com">Genially</a> es en incorporar <a href="https://www.testcontainers.org/">Testcontainers</a> en algunas de nuestras aplicaciones. Una librería que conocía del mundo Java pero que ahora está disponible en otros ecosistemas, incluido el de <a href="https://node.testcontainers.org/">Node</a>.</p>

<p>Testcontainers utiliza Docker por debajo y nos ofrece la posibilidad de tener instancias de usar y tirar para nuestras suites de test, así que de esta manera podemos tener <strong>tests de integración autocontenidos</strong>. Esto facilita el trabajo en local y simplifica enormemente la automatización en los pipelines de CI al no tener que ir montando infraestructura específica.</p>

<p>Normalmente nos interesa tener instancias limpias con las que trabajar y que cada test prepare en el <em>arrange</em> el estado que espera. Pero me encontré en la necesidad de tener que cargar varios scripts a una base de datos MySql y vi que las imágenes de Docker de Postgres, MySql, Mongo… soportan inicializar datos la primera vez que se instancia un contenedor, esto se hace ejecutando los scripts que se encuentren en el directorio <code class="language-plaintext highlighter-rouge">/docker-entrypoint-initdb.d</code></p>

<p>Así, que lo que tenemos que hacer es copiar esos scripts programáticamente antes de inicializar el contenedor usando <code class="language-plaintext highlighter-rouge">withCopyFilesToContainer</code> <a href="https://node.testcontainers.org/features/containers/#with-filescontent">del api de Testcontainers</a>.</p>

<p>Este sería un ejemplo:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">container</span> <span class="o">=</span> <span class="k">await</span> <span class="k">new</span> <span class="nx">GenericContainer</span><span class="p">(</span><span class="dl">'</span><span class="s1">mysql:8.0</span><span class="dl">'</span><span class="p">)</span>
   <span class="p">.</span><span class="nx">withExposedPorts</span><span class="p">(</span><span class="mi">3306</span><span class="p">)</span>
   <span class="p">.</span><span class="nx">withEnvironment</span><span class="p">({</span>
     <span class="na">MYSQL_DATABASE</span><span class="p">:</span> <span class="dl">'</span><span class="s1">test_db</span><span class="dl">'</span><span class="p">,</span>
     <span class="na">MYSQL_ROOT_PASSWORD</span><span class="p">:</span> <span class="dl">'</span><span class="s1">a_root_password</span><span class="dl">'</span><span class="p">,</span>
     <span class="na">MYSQL_USER</span><span class="p">:</span> <span class="dl">'</span><span class="s1">an_user</span><span class="dl">'</span><span class="p">,</span>
     <span class="na">MYSQL_PASSWORD</span><span class="p">:</span> <span class="dl">'</span><span class="s1">a_password</span><span class="dl">'</span><span class="p">,</span>
   <span class="p">})</span>
   <span class="p">.</span><span class="nx">withCopyFilesToContainer</span><span class="p">([</span>
     <span class="p">{</span>
       <span class="na">source</span><span class="p">:</span> <span class="dl">'</span><span class="s1">./dump/foo.sql</span><span class="dl">'</span><span class="p">,</span>
       <span class="na">target</span><span class="p">:</span> <span class="dl">'</span><span class="s1">/docker-entrypoint-initdb.d/1.sql</span><span class="dl">'</span><span class="p">,</span>
     <span class="p">},</span>
     <span class="p">{</span>
       <span class="na">source</span><span class="p">:</span> <span class="dl">'</span><span class="s1">./dump/bar.sql</span><span class="dl">'</span><span class="p">,</span>
       <span class="na">target</span><span class="p">:</span> <span class="dl">'</span><span class="s1">/docker-entrypoint-initdb.d/2.sql</span><span class="dl">'</span><span class="p">,</span>
     <span class="p">}</span>
   <span class="p">])</span>
   <span class="p">.</span><span class="nx">start</span><span class="p">();</span>
</code></pre></div></div>

<p>Por cierto, hay que tener en cuenta que si hay varios ficheros de scripts el orden de ejecución será por orden alfabético.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Una de las cosas en la que ando ayudando últimamente en Genially es en incorporar Testcontainers en algunas de nuestras aplicaciones Node. Y necesitaba precargar datos en una base de datos MySQL.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://danilat.com/img/posts/testcontainers/logo.png" /><media:content medium="image" url="https://danilat.com/img/posts/testcontainers/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>