<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hazel Saenz</title>
    <description>The latest articles on DEV Community by Hazel Saenz (@hsaenzg).</description>
    <link>https://dev.to/hsaenzg</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F852908%2Fa3a10986-307b-4a35-8adf-eeb9a583e6a9.jpg</url>
      <title>DEV Community: Hazel Saenz</title>
      <link>https://dev.to/hsaenzg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hsaenzg"/>
    <language>en</language>
    <item>
      <title>Multi-step workflows que sobreviven fallos</title>
      <dc:creator>Hazel Saenz</dc:creator>
      <pubDate>Thu, 23 Apr 2026 23:23:14 +0000</pubDate>
      <link>https://dev.to/aws/multi-step-workflows-que-sobreviven-fallos-7ag</link>
      <guid>https://dev.to/aws/multi-step-workflows-que-sobreviven-fallos-7ag</guid>
      <description>&lt;p&gt;&lt;em&gt;Cómo los checkpoints automáticos en Lambda Durable Functions mantienen tu progreso incluso cuando todo se rompe.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;En el &lt;a href="https://dev.to/aws/lambda-durable-functions-ia-el-combo-que-no-sabias-que-necesitabas"&gt;artículo anterior&lt;/a&gt; construimos una Durable Function de procesamiento de imágenes paso a paso. Instalamos el Kiro Power, generamos el código con IA, testeamos localmente, y deployamos a AWS con CDK.&lt;/p&gt;

&lt;p&gt;Todo funcionó perfecto.&lt;/p&gt;

&lt;p&gt;Y ese es el problema.&lt;/p&gt;

&lt;p&gt;En producción, las cosas se rompen. APIs que no responden, timeouts, servicios caídos, datos corruptos. La pregunta no es &lt;em&gt;si&lt;/em&gt; algo va a fallar, sino &lt;em&gt;cuándo&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;La promesa central de Durable Functions es que tu workflow sobrevive a los fallos sin perder progreso. Pero, ¿cómo funciona eso realmente? ¿Qué pasa cuando un step falla a mitad de ejecución? ¿Cómo sabe la función dónde retomar?&lt;/p&gt;

&lt;p&gt;En este artículo vamos a &lt;strong&gt;romper cosas a propósito&lt;/strong&gt;. Vamos a simular fallos reales, ver cómo el replay model recupera la ejecución, configurar estrategias de reintento, y aprender a manejar errores dentro del contexto durable.&lt;/p&gt;

&lt;p&gt;Mismo proyecto de procesamiento de imágenes. Pero esta vez, en modo destrucción.&lt;/p&gt;

&lt;h2&gt;
  
  
  El replay model por dentro
&lt;/h2&gt;

&lt;p&gt;En el artículo anterior mencionamos que cada &lt;code&gt;context.step()&lt;/code&gt; es un checkpoint. Pero no explicamos qué pasa por debajo. Ahora sí.&lt;/p&gt;

&lt;p&gt;Cuando una Durable Function se ejecuta por primera vez, el flujo es lineal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6uv73835jey46hli6dgs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6uv73835jey46hli6dgs.png" alt="Primera ejecución: flujo lineal con checkpoints" width="800" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Entra el evento&lt;/li&gt;
&lt;li&gt;Ejecuta el Step 1 → guarda el resultado como checkpoint&lt;/li&gt;
&lt;li&gt;Ejecuta el Step 2 → guarda el resultado como checkpoint&lt;/li&gt;
&lt;li&gt;Ejecuta el Step 3 → guarda el resultado como checkpoint&lt;/li&gt;
&lt;li&gt;Retorna el resultado final&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Hasta aquí, nada especial. Parece una función normal.&lt;/p&gt;

&lt;p&gt;Pero ahora imagina que el Step 3 falla. Un timeout, un error de red, lo que sea. La función se cae.&lt;/p&gt;

&lt;p&gt;Cuando la función se reintenta (ya sea por el retry automático de Lambda o por una nueva invocación), esto es lo que pasa:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Entra el evento (el mismo)&lt;/li&gt;
&lt;li&gt;Llega al Step 1 → &lt;strong&gt;ya tiene checkpoint&lt;/strong&gt; → salta la ejecución, usa el resultado guardado&lt;/li&gt;
&lt;li&gt;Llega al Step 2 → &lt;strong&gt;ya tiene checkpoint&lt;/strong&gt; → salta la ejecución, usa el resultado guardado&lt;/li&gt;
&lt;li&gt;Llega al Step 3 → &lt;strong&gt;no tiene checkpoint&lt;/strong&gt; → lo ejecuta de nuevo&lt;/li&gt;
&lt;li&gt;Si funciona → guarda checkpoint → continúa&lt;/li&gt;
&lt;li&gt;Si falla otra vez → repite el ciclo&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Eso es el replay model. No es magia. Es un sistema de checkpoints que guarda el resultado de cada step y lo reutiliza en los reintentos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzzwn1gn766bddpdmbh0o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzzwn1gn766bddpdmbh0o.png" alt="Diagrama del ciclo del replay model" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lo importante: &lt;strong&gt;el código se ejecuta desde el principio cada vez&lt;/strong&gt;. Pero los steps que ya completaron se saltan instantáneamente porque el resultado ya está guardado. El código fuera de los steps sí se vuelve a ejecutar (por eso la regla de oro del determinismo que vimos en el artículo anterior).&lt;/p&gt;

&lt;h2&gt;
  
  
  Anatomía de un checkpoint
&lt;/h2&gt;

&lt;p&gt;¿Qué se guarda exactamente en cada &lt;code&gt;context.step()&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Cada checkpoint almacena:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;El nombre del step&lt;/strong&gt;: el string que le pasas como primer argumento (&lt;code&gt;'validate-file'&lt;/code&gt;, &lt;code&gt;'resize-image'&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;El resultado&lt;/strong&gt;: lo que retorna la función async dentro del step&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;El estado&lt;/strong&gt;: completado, fallido, o en progreso&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timestamps&lt;/strong&gt;: cuándo empezó y cuándo terminó
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Cuando este step se completa exitosamente...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;validate-file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// ...el checkpoint guarda algo como:&lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//   name: 'validate-file',&lt;/span&gt;
&lt;span class="c1"&gt;//   status: 'completed',&lt;/span&gt;
&lt;span class="c1"&gt;//   result: { format: 'jpeg', width: 1920, height: 1080, size: 2456789 },&lt;/span&gt;
&lt;span class="c1"&gt;//   startedAt: '2026-04-20T10:00:00.000Z',&lt;/span&gt;
&lt;span class="c1"&gt;//   completedAt: '2026-04-20T10:00:01.234Z'&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Consideraciones sobre el tamaño de los datos
&lt;/h3&gt;

&lt;p&gt;El resultado de cada step se serializa y se guarda. Eso significa que si tu step retorna un buffer de 5 MB, esos 5 MB se guardan en el checkpoint.&lt;/p&gt;

&lt;p&gt;Recuerda que Durable Functions maneja datos entre pasos a través de checkpoints serializados. La &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/durable-best-practices.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;documentación oficial&lt;/a&gt; recomienda mantener el estado mínimo y usar almacenamiento externo (S3, DynamoDB) para datos grandes. No es infinito.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Buena práctica&lt;/strong&gt;: retorna solo lo que el siguiente step necesita. Si procesaste una imagen, no retornes el buffer completo. Guárdalo en S3 y retorna la key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Esto puede ser problemático con imágenes grandes&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;resized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Puede ser varios MB&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Mejor: guarda en S3 y retorna la referencia&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resizedKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`processed/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-resized&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;outputBucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Solo unos bytes&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rompiendo cosas a propósito — Escenario 1: Fallo transitorio
&lt;/h2&gt;

&lt;p&gt;Basta de teoría. Vamos a romper algo.&lt;/p&gt;

&lt;p&gt;Usaremos el mismo proyecto de procesamiento de imágenes del &lt;a href="https://dev.to/aws/lambda-durable-functions-ia-el-combo-que-no-sabias-que-necesitabas"&gt;artículo anterior&lt;/a&gt;. Si no lo tienes, puedes clonarlo desde &lt;a href="https://github.com/hsaenzG/durable-funtions-demo" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simulando un fallo de red en el resize
&lt;/h3&gt;

&lt;p&gt;Vamos a modificar el step de resize para que falle las primeras dos veces, simulando un error de red transitorio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Contador de intentos (solo para demostración)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;resizeAttempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resizedBase64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize-image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;resizeAttempts&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Simular fallo de red en los primeros 2 intentos&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resizeAttempts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Resize attempt &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;resizeAttempts&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; failed - simulating network error`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ENETUNREACH: network is unreachable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Resize attempt &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;resizeAttempts&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; succeeded`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inside&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;withoutEnlargement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resizedKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;outputPrefix&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-resized&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;outputBucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resizedKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ContentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`image/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;resized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Qué pasa cuando lo ejecutas
&lt;/h3&gt;

&lt;p&gt;Invoca la función:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws lambda invoke &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--function-name&lt;/span&gt; &lt;span class="s1"&gt;'TU-FUNCION:prod'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--invocation-type&lt;/span&gt; RequestResponse &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--durable-execution-name&lt;/span&gt; &lt;span class="s2"&gt;"test-failure-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--payload&lt;/span&gt; &lt;span class="s1"&gt;'{"bucket":"TU-BUCKET","key":"photos/test.jpg"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cli-binary-format&lt;/span&gt; raw-in-base64-out &lt;span class="se"&gt;\&lt;/span&gt;
  response.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En los logs de CloudWatch vas a ver algo como esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026-04-23T10:00:00.000Z [INFO] Step 'validate-file' - executing
2026-04-23T10:00:01.234Z [INFO] File validated: jpeg, 1920x1080, 2.4MB
2026-04-23T10:00:01.235Z [INFO] Step 'validate-file' - completed ✓
2026-04-23T10:00:01.300Z [INFO] Step 'resize-image' - executing
2026-04-23T10:00:01.301Z [ERROR] Resize attempt 1 failed - simulating network error
2026-04-23T10:00:02.500Z [INFO] Step 'resize-image' - retrying (attempt 2)
2026-04-23T10:00:02.501Z [ERROR] Resize attempt 2 failed - simulating network error
2026-04-23T10:00:04.800Z [INFO] Step 'resize-image' - retrying (attempt 3)
2026-04-23T10:00:05.120Z [INFO] Resize attempt 3 succeeded
2026-04-23T10:00:05.121Z [INFO] Step 'resize-image' - completed ✓
2026-04-23T10:00:05.200Z [INFO] Step 'apply-watermark' - executing
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fíjate en lo que &lt;strong&gt;no&lt;/strong&gt; aparece: no hay un segundo &lt;code&gt;Step 'validate-file' - executing&lt;/code&gt;. El step de validación se completó en el primer intento, se guardó el checkpoint, y en los reintentos del resize &lt;strong&gt;no se volvió a ejecutar&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Eso es el replay model haciendo su trabajo. El progreso no se pierde.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffyuyvoqg853zj720w66b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffyuyvoqg853zj720w66b.png" alt="replay model" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Estrategias de reintento
&lt;/h2&gt;

&lt;p&gt;En el ejemplo anterior, el SDK reintentó automáticamente. Pero en producción necesitas control fino sobre cómo y cuándo reintentar.&lt;/p&gt;

&lt;p&gt;Cada step acepta un &lt;code&gt;retryStrategy&lt;/code&gt; que te permite configurar. El SDK provee un helper &lt;code&gt;createRetryStrategy()&lt;/code&gt; para construir estrategias con configuración declarativa:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRetryStrategy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JitterStrategy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws/durable-execution-sdk-js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;retryStrategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRetryStrategy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;maxAttempts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="c1"&gt;// Total de intentos (incluyendo el primero)&lt;/span&gt;
  &lt;span class="na"&gt;initialDelay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;     &lt;span class="c1"&gt;// Espera inicial: 1 segundo&lt;/span&gt;
  &lt;span class="na"&gt;backoffRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="c1"&gt;// Multiplica el tiempo de espera entre reintentos: 1s → 2s → 4s → 8s&lt;/span&gt;
  &lt;span class="na"&gt;maxDelay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;        &lt;span class="c1"&gt;// Tope máximo: 30 segundos&lt;/span&gt;
  &lt;span class="na"&gt;jitter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JitterStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// Variación aleatoria &lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;También puedes escribir una estrategia custom como función directa:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;call-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;callAPI&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;retryStrategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attemptCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attemptCount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;shouldRetry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attemptCount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;shouldRetry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Exponential backoff: por qué importa
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ei84dbqo1d7iv1qkr29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ei84dbqo1d7iv1qkr29.png" alt="Exporienntial backoff" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si un servicio está caído y reintentamos inmediatamente, lo único que logramos es bombardearlo con requests mientras intenta recuperarse. Es como tocar el timbre 50 veces cuando alguien no abre la puerta.&lt;/p&gt;

&lt;p&gt;El exponential backoff resuelve esto: cada reintento espera más que el anterior.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intento 1: falla → espera 1 segundo&lt;/li&gt;
&lt;li&gt;Intento 2: falla → espera 2 segundos&lt;/li&gt;
&lt;li&gt;Intento 3: falla → espera 4 segundos&lt;/li&gt;
&lt;li&gt;Intento 4: falla → espera 8 segundos&lt;/li&gt;
&lt;li&gt;Intento 5: falla → se rinde&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esto le da tiempo al servicio para recuperarse sin ahogarlo con requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aplicando retry strategy al step de watermark
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;watermarked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apply-watermark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resizedBase64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Llamada a un servicio externo de watermark&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://watermark-api.example.com/apply&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/octet-stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Watermark API failed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;watermarkedBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;watermarkedKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;outputPrefix&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-watermarked&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;outputBucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;watermarkedKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;watermarkedBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;watermarkedKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;retryStrategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;createRetryStrategy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;maxAttempts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;initialDelay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;backoffRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;maxDelay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Qué errores reintentar y cuáles no
&lt;/h3&gt;

&lt;p&gt;No todos los errores merecen un reintento. Si el servicio devuelve un &lt;code&gt;404 Not Found&lt;/code&gt;, reintentarlo 5 veces no va a cambiar nada. Pero un &lt;code&gt;503 Service Unavailable&lt;/code&gt; sí puede resolverse con tiempo.&lt;/p&gt;

&lt;p&gt;Regla general:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Error&lt;/th&gt;
&lt;th&gt;¿Reintentar?&lt;/th&gt;
&lt;th&gt;Por qué&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Timeout / ENETUNREACH&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Problema de red transitorio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;429 Too Many Requests&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Rate limiting, esperar ayuda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;500 Internal Server Error&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;El servidor puede recuperarse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;503 Service Unavailable&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Servicio temporalmente caído&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;400 Bad Request&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Los datos están mal, reintentar no cambia nada&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;404 Not Found&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;El recurso no existe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;403 Forbidden&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;No tienes permisos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formato de archivo inválido&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;El archivo no va a cambiar&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Para errores que no deberían reintentarse, puedes lanzar un error específico y manejarlo con try/catch (lo vemos en el siguiente escenario).&lt;/p&gt;

&lt;h2&gt;
  
  
  Rompiendo cosas a propósito — Escenario 2: Fallo permanente
&lt;/h2&gt;

&lt;p&gt;No todos los fallos se resuelven reintentando. A veces el archivo está corrupto, el formato no es soportado, o los datos de entrada son inválidos. Reintentar 100 veces no va a arreglar un PNG roto.&lt;/p&gt;

&lt;p&gt;Para estos casos necesitas &lt;strong&gt;manejo de errores explícito&lt;/strong&gt; con try/catch y &lt;strong&gt;lógica de compensación&lt;/strong&gt;. El enfoque más robusto es envolver los steps que pueden fallar permanentemente en un try/catch y ejecutar la compensación en un step separado:&lt;/p&gt;

&lt;h3&gt;
  
  
  Simulando un archivo corrupto
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withDurableExecution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ImageEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DurableContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputBucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputBucket&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputPrefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputPrefix&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;processed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.[^&lt;/span&gt;&lt;span class="sr"&gt;.&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.[^&lt;/span&gt;&lt;span class="sr"&gt;.&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;)?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;imageBuffer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Step 1: Validar el archivo&lt;/span&gt;
    &lt;span class="nx"&gt;imageBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;validate-file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}));&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`File not found: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transformToByteArray&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tiff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s2"&gt;`Unsupported image format: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Supported: jpeg, png, webp, tiff`&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Compensación: notificar y limpiar&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handle-validation-failure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Validation failed, executing compensation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="c1"&gt;// Notificar al usuario&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublishCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;TopicArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NOTIFICATION_TOPIC_ARN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Image Processing Failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;}));&lt;/span&gt;

      &lt;span class="c1"&gt;// Mover el archivo a una carpeta de errores para revisión&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CopyObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;CopySource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`errors/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Si llegamos aquí, la validación pasó. Continuar con el procesamiento...&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resizedBase64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize-image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... resize logic&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// ... resto del workflow&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Qué está pasando aquí
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;El step &lt;code&gt;validate-file&lt;/code&gt; detecta que el formato no es soportado y lanza un error&lt;/li&gt;
&lt;li&gt;El error &lt;strong&gt;no se reintenta&lt;/strong&gt; porque es un fallo permanente (el archivo no va a cambiar)&lt;/li&gt;
&lt;li&gt;El catch ejecuta un step de compensación: &lt;code&gt;handle-validation-failure&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;La compensación notifica al usuario vía SNS y mueve el archivo a una carpeta de errores&lt;/li&gt;
&lt;li&gt;La función retorna un resultado de fallo limpio&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Fíjate que la compensación también está dentro de un &lt;code&gt;context.step()&lt;/code&gt;. Esto es importante: si la función se cae mientras envía la notificación, el replay model se encarga de que no se pierda.&lt;/p&gt;

&lt;h3&gt;
  
  
  El patrón de compensación
&lt;/h3&gt;

&lt;p&gt;La compensación es el "plan B" cuando algo falla de forma irrecuperable. No es solo loggear el error y seguir adelante. Es &lt;strong&gt;deshacer o mitigar&lt;/strong&gt; el impacto del fallo.&lt;/p&gt;

&lt;p&gt;Ejemplos de compensación:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fallo&lt;/th&gt;
&lt;th&gt;Compensación&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Procesamiento de imagen falla&lt;/td&gt;
&lt;td&gt;Notificar al usuario, mover archivo a revisión&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cobro duplicado&lt;/td&gt;
&lt;td&gt;Emitir reembolso automático&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Envío de email falla permanentemente&lt;/td&gt;
&lt;td&gt;Registrar en cola de dead letters, alertar al equipo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Creación de recurso falla a mitad&lt;/td&gt;
&lt;td&gt;Eliminar recursos parcialmente creados&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La clave: cada step de compensación debe ser &lt;strong&gt;idempotente&lt;/strong&gt; (ya llegaremos a eso). Si la compensación se ejecuta dos veces, el resultado debe ser el mismo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Errores fuera de los steps
&lt;/h2&gt;

&lt;p&gt;Hay un caso que confunde a muchos: ¿qué pasa cuando el error ocurre &lt;strong&gt;fuera&lt;/strong&gt; de un &lt;code&gt;context.step()&lt;/code&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withDurableExecution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ImageEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DurableContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ⚠️ Este código está FUERA de un step&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Si event.config no es JSON válido... 💥&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;validate-file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si &lt;code&gt;JSON.parse&lt;/code&gt; falla, la función se cae &lt;strong&gt;antes&lt;/strong&gt; de llegar a cualquier step. No hay checkpoint. No hay replay. No hay recuperación elegante.&lt;/p&gt;

&lt;p&gt;Según la documentación oficial: cuando un error ocurre fuera de un step, &lt;strong&gt;el SDK marca la ejecución como &lt;code&gt;FAILED&lt;/code&gt; y no la reintenta automáticamente&lt;/strong&gt;. La ejecución termina ahí. No hay loop infinito de reintentos, pero tampoco hay recuperación. Tu workflow simplemente muere.&lt;/p&gt;

&lt;h3&gt;
  
  
  La regla: todo lo que puede fallar va dentro de un step
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withDurableExecution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ImageEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DurableContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ✅ Ahora el parseo está dentro de un step&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;parse-config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Retornar config por defecto si el parseo falla&lt;/span&gt;
      &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid config, using defaults&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;maxHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;validate-file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora si el parseo falla, el step lo maneja con un fallback. Y si la función se reintenta después de que este step completó, el resultado (ya sea el config parseado o el default) ya está en el checkpoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regla práctica&lt;/strong&gt;: si una línea de código puede lanzar una excepción, debería estar dentro de un step. El código fuera de los steps debe ser puramente determinístico: asignaciones simples, cálculos con datos del evento, cosas que nunca fallan.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué es idempotencia?
&lt;/h2&gt;

&lt;p&gt;Antes de hablar de por qué la idempotencia importa en Durable Functions, hay que entender el concepto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Una operación es idempotente cuando ejecutarla una vez produce exactamente el mismo resultado que ejecutarla múltiples veces con los mismos inputs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Suena abstracto. Vamos con ejemplos.&lt;/p&gt;

&lt;h3&gt;
  
  
  La analogía del elevador
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3jg6zq46svg1qsxeq48.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq3jg6zq46svg1qsxeq48.png" alt="Idempotencia: operación idempotente vs no idempotente" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Presionas el botón del elevador. El elevador viene. Presionas el botón 10 veces más. El elevador sigue viniendo una sola vez. No vienen 11 elevadores.&lt;/p&gt;

&lt;p&gt;Presionar el botón del elevador es una operación idempotente.&lt;/p&gt;

&lt;p&gt;Ahora imagina un botón de "comprar" en una tienda online. Lo presionas una vez: se cobra $10. Lo presionas otra vez: se cobran otros $10. Ahora debes $20 por un solo producto.&lt;/p&gt;

&lt;p&gt;Presionar el botón de comprar &lt;strong&gt;no&lt;/strong&gt; es idempotente (a menos que lo diseñes para que lo sea).&lt;/p&gt;

&lt;h3&gt;
  
  
  En software
&lt;/h3&gt;

&lt;p&gt;Algunas operaciones son &lt;strong&gt;naturalmente idempotentes&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ PUT: actualizar un registro con los mismos datos siempre da el mismo resultado&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutItemCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;S&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;S&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ana&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;S&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ana@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="c1"&gt;// Ejecutar esto 1 vez o 100 veces → el registro queda igual&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ DELETE por ID: eliminar algo que ya no existe no causa error&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DeleteObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-bucket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;photos/test.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="c1"&gt;// Ejecutar esto 1 vez o 100 veces → el archivo no está&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Escribir en S3 con la misma key: sobrescribe con el mismo contenido&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-bucket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;processed/photo-resized.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Key fija y determinística&lt;/span&gt;
  &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resizedBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="c1"&gt;// Ejecutar esto 1 vez o 100 veces → el mismo archivo en la misma ubicación&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otras operaciones &lt;strong&gt;requieren diseño explícito&lt;/strong&gt; para ser idempotentes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ INSERT: cada ejecución crea un registro nuevo&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutItemCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;S&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// ID diferente cada vez&lt;/span&gt;
    &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;N&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;10&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="c1"&gt;// Ejecutar 3 veces → 3 órdenes diferentes. Desastre.&lt;/span&gt;

&lt;span class="c1"&gt;// ❌ Envío de email: cada ejecución envía un email nuevo&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SendEmailCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;Destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ToAddresses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tu orden fue procesada&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="c1"&gt;// Ejecutar 3 veces → 3 emails. El usuario piensa que tiene 3 órdenes.&lt;/span&gt;

&lt;span class="c1"&gt;// ❌ Cobro: cada ejecución cobra de nuevo&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;charges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;usd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Ejecutar 3 veces → 3 cobros de $10. El usuario paga $30.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cómo hacer idempotente lo que no lo es
&lt;/h3&gt;

&lt;p&gt;El patrón más común es usar una &lt;strong&gt;idempotency key&lt;/strong&gt;: un identificador único que garantiza que la operación solo se ejecuta una vez.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ INSERT con idempotency key&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orderId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`order-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Determinístico&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutItemCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;S&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;orderId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// Siempre el mismo para el mismo input&lt;/span&gt;
    &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;N&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;10&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;ConditionExpression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attribute_not_exists(orderId)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Solo inserta si no existe&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Cobro con idempotency key&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;charges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;usd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;idempotencyKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`charge-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Stripe ignora duplicados&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Email con deduplicación&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`email-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-confirmation`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alreadySent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;checkIfEmailSent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emailId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Consulta a DynamoDB&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;alreadySent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SendEmailCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;markEmailAsSent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emailId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La diferencia entre operaciones naturalmente idempotentes y las que requieren diseño es crucial. Las primeras "simplemente funcionan" en un contexto de reintentos. Las segundas necesitan que tú pienses en el caso de ejecución múltiple.&lt;/p&gt;

&lt;h2&gt;
  
  
  Idempotencia en Durable Functions: la regla que nadie te dice
&lt;/h2&gt;

&lt;p&gt;Ahora que entiendes qué es idempotencia, conectemos con el replay model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cada step en una Durable Function debe ser idempotente.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;¿Por qué? Porque el replay model puede re-ejecutar un step si:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;La función se cae después de que el step ejecutó pero &lt;strong&gt;antes&lt;/strong&gt; de que el checkpoint se guardara&lt;/li&gt;
&lt;li&gt;Hay un error transitorio en el sistema de checkpoints&lt;/li&gt;
&lt;li&gt;La función hace timeout justo en el momento del checkpoint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En estos casos (poco frecuentes pero reales), el step se ejecuta de nuevo con los mismos inputs. Si el step no es idempotente, tienes un problema.&lt;/p&gt;

&lt;h3&gt;
  
  
  El ejemplo del procesamiento de imágenes
&lt;/h3&gt;

&lt;p&gt;Veamos dos versiones del step que sube la imagen procesada a S3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ NO idempotente: genera un nombre aleatorio cada vez&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;savedKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;save-to-s3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;randomId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Diferente en cada ejecución&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`processed/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;randomId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.jpg`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;outputBucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;processedBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Si se ejecuta 2 veces: 2 archivos diferentes en S3&lt;/span&gt;
&lt;span class="c1"&gt;// processed/a1b2c3d4.jpg Y processed/e5f6g7h8.jpg&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Idempotente: usa un nombre determinístico basado en el input&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;savedKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;save-to-s3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`processed/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-final&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Siempre igual para el mismo input&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;outputBucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;processedBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Si se ejecuta 2 veces: el mismo archivo se sobrescribe&lt;/span&gt;
&lt;span class="c1"&gt;// processed/photo-final.jpg (una sola copia)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La diferencia es sutil pero crítica. En el primer caso, cada ejecución deja basura en S3. Y esa basura se acumula: archivos huérfanos que nadie usa pero que siguen sumando a tu factura de almacenamiento mes a mes. En el segundo, no importa cuántas veces se ejecute: el resultado es el mismo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Checklist de idempotencia para tus steps
&lt;/h3&gt;

&lt;p&gt;Antes de deployar, revisa cada step con estas preguntas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;¿El step genera IDs aleatorios?&lt;/strong&gt; → Usa IDs determinísticos basados en el input&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;¿El step inserta registros en una base de datos?&lt;/strong&gt; → Usa &lt;code&gt;ConditionExpression&lt;/code&gt; o upsert&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;¿El step envía emails o notificaciones?&lt;/strong&gt; → Implementa deduplicación&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;¿El step cobra dinero?&lt;/strong&gt; → Usa idempotency keys del proveedor de pagos&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;¿El step crea recursos en la nube?&lt;/strong&gt; → Usa nombres determinísticos o verifica existencia antes de crear&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;¿El step llama a una API externa que modifica estado?&lt;/strong&gt; → Verifica si la API soporta idempotency keys&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si respondiste "sí" a alguna y no tienes protección, tu step no es idempotente. Y en un sistema con reintentos automáticos, eso es una bomba de tiempo.&lt;/p&gt;

&lt;h3&gt;
  
  
  AT_MOST_ONCE_PER_RETRY: la red de seguridad del SDK
&lt;/h3&gt;

&lt;p&gt;Para operaciones críticas que &lt;strong&gt;no pueden&lt;/strong&gt; ejecutarse dos veces (como cobros), el SDK ofrece una opción adicional: &lt;code&gt;AT_MOST_ONCE_PER_RETRY&lt;/code&gt; semantics. Con esta configuración, si un step se interrumpe antes de guardar el checkpoint, el SDK &lt;strong&gt;no lo re-ejecuta&lt;/strong&gt; en la siguiente invocación.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Para operaciones que NUNCA deben duplicarse&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;charge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;charge-payment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;chargeCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;semantics&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AT_MOST_ONCE_PER_RETRY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El trade-off: si el step se interrumpe, puede que la operación se haya ejecutado pero no tengas el resultado. Tendrás que verificar manualmente (por ejemplo, consultar el sistema de pagos). Pero es mejor verificar un cobro que cobrar dos veces.&lt;/p&gt;

&lt;p&gt;Usa &lt;code&gt;AT_MOST_ONCE_PER_RETRY&lt;/code&gt; solo para operaciones donde la duplicación es peor que la incertidumbre. Para la mayoría de los steps, el default &lt;code&gt;AT_LEAST_ONCE_PER_RETRY&lt;/code&gt; con diseño idempotente es la mejor opción.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rompiendo cosas a propósito — Escenario 3: Timeout de la función
&lt;/h2&gt;

&lt;p&gt;Este es el escenario más interesante. ¿Qué pasa cuando la Lambda se queda sin tiempo a mitad del workflow?&lt;/p&gt;

&lt;p&gt;Imagina que tu función tiene un timeout de 30 segundos (el default de Lambda), pero el procesamiento completo tarda 45 segundos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Step 1: validate-file     → 2 segundos  ✓ (checkpoint guardado)
Step 2: resize-image      → 5 segundos  ✓ (checkpoint guardado)
Step 3: apply-watermark   → 10 segundos ✓ (checkpoint guardado)
Step 4: generate-thumbnails → 15 segundos... ⏰ TIMEOUT a los 30 segundos
Step 5: save-to-s3        → nunca se ejecutó
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La función se cae. Pero los primeros 3 steps tienen sus checkpoints guardados.&lt;/p&gt;

&lt;p&gt;Cuando la función se invoca de nuevo (ya sea manualmente o por el retry automático):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Step 1: validate-file     → checkpoint existe → SKIP (0 segundos)
Step 2: resize-image      → checkpoint existe → SKIP (0 segundos)
Step 3: apply-watermark   → checkpoint existe → SKIP (0 segundos)
Step 4: generate-thumbnails → SIN checkpoint → ejecuta (15 segundos) ✓
Step 5: save-to-s3        → ejecuta (3 segundos) ✓
Total: 18 segundos → dentro del timeout ✓
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El workflow completo tardó 45 segundos, pero ninguna invocación individual excedió los 30 segundos. El replay model distribuyó el trabajo entre múltiples invocaciones.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pero espera... ¿y el &lt;code&gt;durableConfig.executionTimeout&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;Aquí hay una distinción importante:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lambda timeout&lt;/strong&gt;: cuánto tiempo puede correr una sola invocación de la función (máximo 15 minutos)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;executionTimeout&lt;/code&gt; en &lt;code&gt;durableConfig&lt;/code&gt;&lt;/strong&gt;: cuánto tiempo puede correr el &lt;strong&gt;workflow completo&lt;/strong&gt; a través de múltiples invocaciones (hasta 1 año)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// En tu CDK stack:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ImageProcessing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODEJS_24_X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handler.handler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;// Cada invocación: máximo 5 minutos&lt;/span&gt;
  &lt;span class="na"&gt;durableConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;executionTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;// Workflow completo: máximo 1 hora&lt;/span&gt;
    &lt;span class="na"&gt;retentionPeriod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto significa que tu workflow puede tardar hasta 1 hora en completarse, distribuido en tantas invocaciones de 5 minutos como necesite. El replay model se encarga de retomar donde quedó cada vez.&lt;/p&gt;

&lt;p&gt;Para workflows de procesamiento de imágenes, 5 minutos por invocación suele ser suficiente. Pero para workflows que involucran aprobaciones humanas o esperas largas (que veremos en el próximo artículo), el &lt;code&gt;executionTimeout&lt;/code&gt; de hasta 1 año es lo que hace posible esos patrones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoreando fallos en la consola
&lt;/h2&gt;

&lt;p&gt;Ya rompiste cosas. Ahora necesitas ver qué pasó.&lt;/p&gt;

&lt;p&gt;La consola de AWS Lambda tiene una pestaña dedicada para Durable Functions: &lt;strong&gt;Durable executions&lt;/strong&gt;. Ahí puedes ver:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lista de ejecuciones&lt;/strong&gt;: todas las ejecuciones con su estado (running, completed, failed)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detalle de cada ejecución&lt;/strong&gt;: qué steps se completaron, cuáles fallaron, cuántos reintentos hubo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Datos de cada checkpoint&lt;/strong&gt;: qué resultado guardó cada step&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeline&lt;/strong&gt;: cuánto tardó cada step y dónde ocurrieron los fallos&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tips para debugging efectivo
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Nombra tus steps de forma descriptiva&lt;/strong&gt;: &lt;code&gt;'validate-file'&lt;/code&gt; es mucho más útil que &lt;code&gt;'step-1'&lt;/code&gt; cuando estás buscando un fallo en la consola.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Usa &lt;code&gt;context.logger&lt;/code&gt;&lt;/strong&gt;: No uses &lt;code&gt;console.log&lt;/code&gt;. El logger del contexto es replay-aware: no duplica logs cuando la función se reintenta. Además, incluye metadata del step actual automáticamente.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ console.log se duplica en cada replay&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Processing image:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ context.logger es replay-aware&lt;/span&gt;
&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Processing image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Incluye contexto en los errores&lt;/strong&gt;: Cuando lanzas un error, incluye datos que te ayuden a diagnosticar.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`Invalid image format. Key: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, `&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s2"&gt;`Size: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bytes, `&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s2"&gt;`First bytes: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Revisa el estado de los checkpoints&lt;/strong&gt;: En la consola, cada step muestra su resultado guardado. Si un step retornó datos inesperados, ahí lo vas a ver sin necesidad de reproducir el error.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Patrones de resiliencia
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxquf38k0atwdotsth0tc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxquf38k0atwdotsth0tc.png" alt="Patrones de resiliencia en Durable Functions" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Antes de cerrar, un resumen de los patrones que cubrimos y algunos adicionales que deberías tener en tu toolkit:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Retry con backoff exponencial
&lt;/h3&gt;

&lt;p&gt;Para fallos transitorios. Ya lo vimos en detalle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;call-external-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apiCall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;retryStrategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;createRetryStrategy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;maxAttempts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;initialDelay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;backoffRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;maxDelay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Compensación
&lt;/h3&gt;

&lt;p&gt;Para fallos permanentes. Ejecuta lógica de "deshacer" o "mitigar".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;charge-payment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chargeUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;send-product&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shipProduct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;refund-payment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refundUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Compensación&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;notify-failure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;notifyUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Circuit breaker manual
&lt;/h3&gt;

&lt;p&gt;Si un servicio externo está fallando consistentemente, no tiene sentido seguir intentando. Puedes implementar un circuit breaker básico verificando el estado del servicio antes de llamarlo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;serviceHealth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;check-service-health&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com/health&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;serviceHealth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// El servicio está caído, no intentar&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fallback-processing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;External service down, using fallback&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;processLocally&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageBuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;external-processing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;processWithExternalService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageBuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Dead letter queue para workflows irrecuperables
&lt;/h3&gt;

&lt;p&gt;Cuando un workflow falla de forma definitiva y la compensación no es suficiente, envía los datos a una cola de dead letters para revisión manual.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ... workflow completo&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;send-to-dlq&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SendMessageCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;QueueUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DLQ_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;MessageBody&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;failedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;executionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto garantiza que ningún workflow se pierde silenciosamente. Alguien (o algo) puede revisar la cola y decidir qué hacer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que aprendiste
&lt;/h2&gt;

&lt;p&gt;El replay model no es magia. Es un sistema de checkpoints que guarda el resultado de cada step y lo reutiliza cuando la función se reintenta. Los steps completados se saltan, los pendientes se ejecutan.&lt;/p&gt;

&lt;p&gt;Los fallos transitorios se manejan con retry strategies configurables por step: exponential backoff, máximo de intentos, y topes de espera. Los fallos permanentes se manejan con try/catch y lógica de compensación: notificar, limpiar, revertir.&lt;/p&gt;

&lt;p&gt;El código fuera de los steps se ejecuta en cada replay. Si puede fallar, debería estar dentro de un step. Si genera valores no determinísticos, definitivamente debería estar dentro de un step.&lt;/p&gt;

&lt;p&gt;Cada step debe ser idempotente. Si se ejecuta dos veces con los mismos inputs, debe producir el mismo resultado. Para operaciones que no son naturalmente idempotentes (inserts, cobros, emails), usa idempotency keys y deduplicación.&lt;/p&gt;

&lt;p&gt;Y la consola de AWS Lambda te da visibilidad completa: qué steps se completaron, cuáles fallaron, cuántos reintentos hubo, y qué datos se guardaron en cada checkpoint.&lt;/p&gt;

&lt;p&gt;Rompimos cosas a propósito. Y el workflow sobrevivió.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;En tres artículos recorrimos el camino completo de Lambda Durable Functions: desde entender cuándo usarlas (y cuándo no), pasando por construir tu primera función con Kiro Power, hasta romper cosas a propósito para entender cómo el replay model mantiene tu progreso.&lt;/p&gt;

&lt;p&gt;Si llegaste hasta aquí, ya tienes las herramientas para construir workflows resilientes en producción. Sabes cómo funcionan los checkpoints, cómo configurar retries con backoff exponencial, cómo manejar fallos permanentes con compensación, y por qué la idempotencia no es opcional.&lt;/p&gt;

&lt;p&gt;Durable Functions tiene más por explorar: callbacks para aprobaciones humanas, waits que pausan ejecución sin costo, ejecución en paralelo con &lt;code&gt;context.map()&lt;/code&gt;, e invocaciones entre funciones con &lt;code&gt;context.invoke()&lt;/code&gt;. La &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;documentación oficial&lt;/a&gt; y el &lt;a href="https://docs.aws.amazon.com/durable-execution/sdk-reference/error-handling/retries?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;SDK de Durable Execution&lt;/a&gt; cubren todo eso en detalle.&lt;/p&gt;

&lt;p&gt;Construye. Rompe. Recupera. Esa es la promesa.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Si quieres profundizar en Durable Functions&lt;/strong&gt;, estos recursos te llevan más lejos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Documentación oficial de Lambda Durable Functions&lt;/a&gt; — El punto de partida para todo: callbacks, waits, ejecución en paralelo, y más&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/durable-best-practices.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Best practices for Lambda durable functions&lt;/a&gt; — Determinismo, idempotencia, manejo de estado, y errores comunes&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/durable-execution/sdk-reference/error-handling/retries?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Durable Execution SDK — Retry Strategies&lt;/a&gt; — Referencia completa de &lt;code&gt;createRetryStrategy&lt;/code&gt;, presets, y custom strategies&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/aws/aws-durable-execution-sdk-js?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Durable Execution SDK (JavaScript)&lt;/a&gt; — Código fuente del SDK, open source&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hsaenzG/durable-funtions-demo" rel="noopener noreferrer"&gt;Proyecto de ejemplo de esta serie (GitHub)&lt;/a&gt; — El código completo del procesamiento de imágenes&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>lambda</category>
      <category>serverless</category>
      <category>espanol</category>
    </item>
    <item>
      <title>Cómo crear un agente de IA desde cero. Open source, local y gratis.</title>
      <dc:creator>Hazel Saenz</dc:creator>
      <pubDate>Wed, 15 Apr 2026 16:15:25 +0000</pubDate>
      <link>https://dev.to/aws/como-crear-un-agente-de-ia-desde-cero-open-source-local-y-gratis-k6d</link>
      <guid>https://dev.to/aws/como-crear-un-agente-de-ia-desde-cero-open-source-local-y-gratis-k6d</guid>
      <description>&lt;p&gt;&lt;em&gt;De un modelo que solo responde a uno que busca, razona y recuerda con código que puedes leer, forkear y contribuir.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Todos hemos usado ChatGPT, Claude, Google Gemini. Les preguntas algo, te responden. Les pides que resuman un texto, lo resumen. Les dices que escriban un correo, lo escriben. Pero hay un límite. Un modelo de lenguaje puede hablar de música. Puede hablar de géneros. Puede hablar de artistas.&lt;/p&gt;

&lt;p&gt;Pero no puede &lt;strong&gt;buscar&lt;/strong&gt; en tu biblioteca de canciones.&lt;br&gt;
No puede &lt;strong&gt;analizar&lt;/strong&gt; el mood de una playlist.&lt;br&gt;
No puede &lt;strong&gt;recordar&lt;/strong&gt; que la semana pasada te armó una playlist de hip-hop para el gym y te encantó.&lt;/p&gt;

&lt;p&gt;Un modelo solo habla.&lt;br&gt;
Un agente &lt;strong&gt;hace cosas&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Y esa diferencia es más importante de lo que parece.&lt;/p&gt;
&lt;h2&gt;
  
  
  ¿Qué es un agente de IA?
&lt;/h2&gt;

&lt;p&gt;Si ya usaste la API de OpenAI o jugaste con modelos en Hugging Face, sabes que un LLM recibe texto y genera texto. Punto. No ejecuta código, no consulta bases de datos, no llama APIs.&lt;/p&gt;

&lt;p&gt;Un agente agrega dos cosas encima de eso:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Herramientas&lt;/strong&gt; (tools): funciones que el modelo puede invocar cualquier función de Python que le expongas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Un agent loop&lt;/strong&gt;: un ciclo donde el modelo razona, decide si necesita llamar una herramienta, la ejecuta, recibe el resultado, y vuelve a razonar hasta tener una respuesta final&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No es magia. Es un &lt;code&gt;while&lt;/code&gt; loop con tool dispatch.&lt;/p&gt;

&lt;p&gt;Imagina que le preguntas a un agente DJ: "Armame una playlist para una cena tranquila con amigos."&lt;/p&gt;

&lt;p&gt;Sin tools, el modelo genera texto basado en su entrenamiento, no conoce tu biblioteca, no sabe qué se lanzó la semana pasada (su conocimiento tiene fecha de corte), y no tiene idea de tus gustos. Con tools, hace esto:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Razona&lt;/strong&gt;: "Necesito buscar canciones con energía baja"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invoca&lt;/strong&gt;: &lt;code&gt;buscar_canciones(mood="chill", genero="jazz")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recibe el resultado&lt;/strong&gt;: una lista de canciones reales de tu biblioteca&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Razona de nuevo&lt;/strong&gt;: "Debería verificar que la energía sea consistente"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invoca&lt;/strong&gt;: &lt;code&gt;analizar_energia(canciones=[...])&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Genera la respuesta final&lt;/strong&gt;: una playlist curada con datos reales&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;El modelo decide qué herramientas invocar y en qué orden. Tú no escribes la orquestación, el modelo la resuelve en runtime. Eso es el enfoque &lt;strong&gt;model-driven&lt;/strong&gt;, y es lo que diferencia a un agente de un pipeline hardcodeado con &lt;code&gt;if/else&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnu1keudgaofqif0foqg3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnu1keudgaofqif0foqg3.png" alt="Agent Loop — el ciclo de razonamiento de un agente" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Por qué open source importa (de verdad)
&lt;/h2&gt;

&lt;p&gt;Cuando hablamos de agentes de IA, hay muchas opciones. Frameworks propietarios, APIs cerradas, SDKs que solo funcionan con un proveedor específico.&lt;/p&gt;

&lt;p&gt;El problema no es que existan. El problema es lo que pierdes cuando los usas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No puedes leer la implementación del agent loop, si el modelo toma una secuencia de tool calls inesperada, no puedes entender por qué ni cambiar el comportamiento&lt;/li&gt;
&lt;li&gt;No puedes modificar el comportamiento del loop cuando algo no funciona como esperas&lt;/li&gt;
&lt;li&gt;Si tu proveedor de modelo no está soportado, no puedes contribuir la integración tú mismo ni aprovechar una que la comunidad ya haya creado&lt;/li&gt;
&lt;li&gt;No puedes contribuir fixes o mejoras que beneficien a otros&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open source no es solo "código gratis". Sí, requiere más responsabilidad de tu parte: tú debuggeas, tú actualizas, tú decides. Pero esa responsabilidad viene con control total. Es &lt;strong&gt;transparencia&lt;/strong&gt;. Es poder abrir el source del agent loop y ver que es un &lt;code&gt;while&lt;/code&gt; loop con tool dispatch. Es poder poner un breakpoint en el ciclo de razonamiento cuando tu agente hace algo inesperado.&lt;/p&gt;

&lt;p&gt;Y eso es exactamente lo que vamos a usar.&lt;/p&gt;
&lt;h2&gt;
  
  
  El stack: Python + Strands Agents + Ollama
&lt;/h2&gt;

&lt;p&gt;Todo lo que vas a ver en este artículo corre con tres cosas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.python.org/" rel="noopener noreferrer"&gt;Python&lt;/a&gt;&lt;/strong&gt;: el lenguaje (licencia &lt;a href="https://docs.python.org/3/license.html" rel="noopener noreferrer"&gt;PSF&lt;/a&gt;, open source)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://strandsagents.com/" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt;&lt;/strong&gt;: un SDK open source (licencia Apache 2.0) para construir agentes de IA&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;&lt;/strong&gt;: un runtime open source (licencia MIT) para correr modelos de lenguaje localmente&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Todo open source. Todo en tu laptop. Sin API keys. Sin vendor lock-in. Sin datos saliendo de tu máquina.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/strands-agents/sdk-python" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt; toma un enfoque &lt;a href="https://strandsagents.com/blog/strands-agents-model-driven-approach/" rel="noopener noreferrer"&gt;model-driven&lt;/a&gt;: tú defines las herramientas como funciones de Python, escribes un system prompt, y el agent loop se encarga de la ejecución. Sin definiciones de pasos, sin grafos de workflow. Solo código.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbwvgh8qkbbtvd1121h7w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbwvgh8qkbbtvd1121h7w.png" alt="Las 4 capas que vamos a construir" width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Ollama: modelos de IA en tu laptop
&lt;/h2&gt;

&lt;p&gt;Antes de construir el agente, necesitamos un modelo de lenguaje corriendo localmente. Y aquí es donde entra &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ollama es un runtime open source para correr LLMs en tu máquina. Si conoces Docker, la analogía es directa: &lt;code&gt;ollama pull&lt;/code&gt; descarga un modelo, &lt;code&gt;ollama serve&lt;/code&gt; lo expone como API REST en &lt;code&gt;localhost:11434&lt;/code&gt;. Sin cuentas, sin API keys, sin datos saliendo de tu red.&lt;/p&gt;
&lt;h3&gt;
  
  
  Instalación
&lt;/h3&gt;

&lt;p&gt;En macOS y Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.com/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En Windows, descarga el instalador desde &lt;a href="https://ollama.com/download" rel="noopener noreferrer"&gt;ollama.com/download&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Descargar y correr un modelo
&lt;/h3&gt;

&lt;p&gt;Para este artículo vamos a usar &lt;a href="https://ollama.com/library/llama3.1" rel="noopener noreferrer"&gt;&lt;code&gt;llama3.1:8b&lt;/code&gt;&lt;/a&gt; — tiene buen soporte para &lt;a href="https://ollama.com/search?c=tools" rel="noopener noreferrer"&gt;tool-calling&lt;/a&gt; (que es lo que necesitamos para que el agente invoque funciones) y maneja español razonablemente bien:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull llama3.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Son unos 4.9 GB. Solo necesitas hacerlo una vez.&lt;/p&gt;

&lt;p&gt;Ahora levanta el servidor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Si instalaste la app de macOS, el servidor ya está corriendo en segundo plano y no necesitas ejecutar &lt;code&gt;ollama serve&lt;/code&gt;. Solo es necesario si instalaste con Homebrew o en Linux.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ollama queda escuchando en &lt;code&gt;http://localhost:11434&lt;/code&gt;. Puedes verificar con un &lt;code&gt;curl http://localhost:11434/api/tags&lt;/code&gt; para ver los modelos disponibles.&lt;/p&gt;

&lt;p&gt;Ollama soporta &lt;a href="https://ollama.com/library" rel="noopener noreferrer"&gt;muchos modelos&lt;/a&gt;: Llama, Mistral, Phi, Gemma, y más. Si tu máquina tiene GPU, los corre acelerados automáticamente. Si no, usa CPU (más lento, pero funciona).&lt;/p&gt;

&lt;p&gt;Lo importante: &lt;strong&gt;el modelo corre en tu máquina&lt;/strong&gt;. Tus datos no salen de tu laptop. Y el código de Ollama es open source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capa 1: Un agente que solo habla
&lt;/h2&gt;

&lt;p&gt;Empecemos por lo más simple. Un agente que es solo un modelo + un prompt.&lt;/p&gt;

&lt;p&gt;Primero, instala el SDK de Strands con soporte para Ollama.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Strands Agents requiere &lt;strong&gt;Python 3.10 o superior&lt;/strong&gt;. El Python que viene preinstalado en macOS (3.9.6) no es compatible. Si no tienes una versión reciente, instálala con &lt;code&gt;brew install python@3.13&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Crea un entorno virtual para no contaminar tu sistema. Con pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s1"&gt;'strands-agents[ollama]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O si usas &lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;uv&lt;/a&gt; (más rápido y gestiona Python por ti):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv venv &lt;span class="nt"&gt;--python&lt;/span&gt; 3.13
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s1"&gt;'strands-agents[ollama]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora, el código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.models.ollama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OllamaModel&lt;/span&gt;

&lt;span class="n"&gt;modelo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OllamaModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama3.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;dj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;modelo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Eres un DJ y curador musical experto.
    Respondes en español, con onda y buen gusto.
    Recomiendas música basándote en el mood, la ocasión, y los gustos del usuario.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;dj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Recomiéndame algo para escuchar mientras programo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cinco líneas relevantes. Eso es un agente.&lt;/p&gt;

&lt;p&gt;Bueno, técnicamente es un modelo con un prompt — todavía no tiene herramientas, así que solo responde con su conocimiento general. Si le pides una playlist con canciones de tu biblioteca, va a alucinar títulos que suenan razonables pero no existen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Un agente es un modelo + un prompt + un loop. Eso es todo.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Y fíjate: el modelo corre local. No hay API key, no hay vendor, no hay datos saliendo de tu laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capa 2: Dale herramientas — la biblioteca de canciones
&lt;/h2&gt;

&lt;p&gt;El agente básico no conoce tu música. Sabe de artistas y géneros en general porque fue entrenado con texto de internet. Pero no sabe que en tu biblioteca tienes ese álbum de la Ley que siempre pones cuando cocinas.&lt;/p&gt;

&lt;p&gt;Para eso necesita una herramienta.&lt;/p&gt;

&lt;p&gt;Pero primero, necesitamos datos. Crea un archivo &lt;code&gt;data/canciones.json&lt;/code&gt; con tu biblioteca de canciones. Cada canción necesita: &lt;code&gt;titulo&lt;/code&gt;, &lt;code&gt;artista&lt;/code&gt;, &lt;code&gt;genero&lt;/code&gt;, &lt;code&gt;mood&lt;/code&gt;, &lt;code&gt;energia&lt;/code&gt; (0-100), y &lt;code&gt;duracion_min&lt;/code&gt;. Aquí tienes un ejemplo para arrancar:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ver ejemplo de data/canciones.json (30 canciones)&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"So What"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Miles Davis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jazz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;9.3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Take Five"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dave Brubeck"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jazz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;5.4&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Blue in Green"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Miles Davis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jazz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"melancólico"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;5.4&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fly Me to the Moon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Frank Sinatra"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jazz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Feeling Good"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Nina Simone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jazz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"energético"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.9&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bohemian Rhapsody"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Queen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rock"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"energético"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;5.9&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hotel California"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Eagles"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rock"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;6.5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Smells Like Teen Spirit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Nirvana"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rock"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"energético"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Under Pressure"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Queen &amp;amp;amp; David Bowie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rock"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"energético"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Creep"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Radiohead"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rock"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"melancólico"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.9&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Gasolina"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Daddy Yankee"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reggaetón"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fiesta"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dákiti"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bad Bunny &amp;amp;amp; Jhay Cortez"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reggaetón"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fiesta"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tití Me Preguntó"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bad Bunny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reggaetón"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fiesta"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pepas"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Farruko"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reggaetón"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fiesta"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;92&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"La Bicicleta"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Shakira &amp;amp;amp; Carlos Vives"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reggaetón"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fiesta"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.8&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Midnight City"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"M83"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"electrónica"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"energético"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Strobe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deadmau5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"electrónica"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;10.3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Around the World"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Daft Punk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"electrónica"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fiesta"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;7.1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Intro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The xx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"electrónica"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Teardrop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Massive Attack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"electrónica"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"melancólico"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;5.3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Disorder"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Joy Division"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"indie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"melancólico"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.6&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Do I Wanna Know?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Arctic Monkeys"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"indie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.6&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Somebody Else"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The 1975"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"indie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"melancólico"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;5.7&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Electric Feel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MGMT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"indie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"energético"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.8&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tongue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MNEK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"indie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"energético"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Only Shallow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My Bloody Valentine"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shoegaze"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"energético"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"When You Sleep"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My Bloody Valentine"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shoegaze"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alison"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Slowdive"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shoegaze"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"melancólico"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;5.1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cherry-coloured Funk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cocteau Twins"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shoegaze"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"titulo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Vapour Trail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"artista"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ride"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"genero"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shoegaze"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mood"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"energético"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"energia"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"duracion_min"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Puedes modificar este archivo con tu propia música. Lo importante es que mantenga la misma estructura.&lt;/p&gt;

&lt;p&gt;Ahora sí, en Strands, crear una herramienta es decorar una función de Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.models.ollama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OllamaModel&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="c1"&gt;# Cargar biblioteca local de canciones
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data/canciones.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;BIBLIOTECA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;buscar_canciones&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;genero&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mood&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;artista&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Busca canciones en la biblioteca musical del usuario.

    Args:
        genero: Género musical (ej: rock, jazz, reggaetón, electrónica)
        mood: Estado de ánimo o energía (ej: chill, fiesta, melancólico, energético)
        artista: Nombre del artista o banda
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;resultados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BIBLIOTECA&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;genero&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;resultados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;resultados&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;genero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;genero&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mood&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;resultados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;resultados&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mood&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mood&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;artista&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;resultados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;resultados&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;artista&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;artista&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;resultados&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No encontré canciones con esos criterios en tu biblioteca.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resultados&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ensure_ascii&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fíjate en el decorador &lt;code&gt;@tool&lt;/code&gt;. Convierte cualquier función de Python en una herramienta que el agente puede invocar. El docstring no es decorativo — el SDK lo &lt;a href="https://strandsagents.com/docs/user-guide/concepts/tools/custom-tools/" rel="noopener noreferrer"&gt;parsea para generar un tool spec&lt;/a&gt; (JSON schema) que es lo que el modelo recibe para decidir cuándo y cómo usar la herramienta. Si el docstring es vago, el modelo no va a saber cuándo llamarla.&lt;/p&gt;

&lt;p&gt;Ahora conectamos la herramienta al agente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;modelo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OllamaModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama3.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;dj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;modelo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Eres un DJ y curador musical experto.
    Usa la herramienta buscar_canciones para encontrar música en la biblioteca del usuario.
    Siempre basa tus recomendaciones en canciones que el usuario realmente tiene.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;buscar_canciones&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;dj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Quiero escuchar jazz mientras trabajo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora cuando le pides jazz para trabajar, el agente no inventa. Llama a &lt;code&gt;buscar_canciones(genero="jazz", mood="chill")&lt;/code&gt;, obtiene canciones reales de tu biblioteca, y arma una recomendación con lo que realmente tienes.&lt;/p&gt;

&lt;p&gt;Pasó de "saber cosas" a "hacer cosas".&lt;/p&gt;

&lt;p&gt;Y el sistema de tools es extensible. Existe un &lt;a href="https://github.com/strands-agents/tools" rel="noopener noreferrer"&gt;paquete de community tools&lt;/a&gt; donde cualquiera puede publicar los suyos. ¿Tienes una API que quieres conectar? Escribe un &lt;code&gt;@tool&lt;/code&gt; y compártelo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capa 3: Más herramientas, más inteligencia
&lt;/h2&gt;

&lt;p&gt;Un tool está bien. Pero la cosa se pone interesante cuando agregas varios.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;analizar_energia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canciones&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Analiza el nivel de energía promedio de una lista de canciones y sugiere el orden ideal.

    Args:
        canciones: Lista de nombres de canciones a analizar
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;energia_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cancion&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;BIBLIOTECA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;energia_map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cancion&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;titulo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cancion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;energia&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;analisis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;titulo&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;canciones&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;energia&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;energia_map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;titulo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;analisis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;titulo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;titulo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;energia&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;energia&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;# Ordenar por energía para un flow natural
&lt;/span&gt;    &lt;span class="n"&gt;analisis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;energia&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;promedio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;energia&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;analisis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;analisis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;analisis&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;energia_promedio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;promedio&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ascendente&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;analisis&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;energia&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;analisis&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;energia&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;descendente&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orden_sugerido&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;titulo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;analisis&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nota&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Energía baja → alta para ir subiendo el mood&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;promedio&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Playlist con buena energía&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;ensure_ascii&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@tool&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;duracion_playlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canciones&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Calcula la duración total de una playlist y sugiere si necesita más canciones.

    Args:
        canciones: Lista de nombres de canciones
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;duracion_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cancion&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;BIBLIOTECA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;duracion_map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cancion&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;titulo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cancion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;duracion_min&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duracion_map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mf"&gt;3.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;canciones&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;canciones&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;canciones&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;duracion_total_min&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;duracion_formato&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;h &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;min&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;min&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sugerencia&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Playlist corta, podrías agregar más canciones&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Buena duración&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;ensure_ascii&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora el agente tiene tres herramientas. Y aquí es donde se ve el agent loop en acción:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;dj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;modelo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Eres un DJ y curador musical experto.
    Usa tus herramientas para armar playlists basadas en la biblioteca real del usuario.
    Considera el mood, la energía, y la duración para crear una experiencia coherente.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;buscar_canciones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;analizar_energia&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duracion_playlist&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;dj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Armame una playlist de una hora para una fiesta en casa&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El agente no ejecuta las herramientas en un orden predefinido. No hay un pipeline que diga "primero busca, luego analiza, luego calcula duración". El modelo recibe los tool specs (nombre, descripción, parámetros) y en cada iteración del loop decide: ¿necesito más información? ¿Qué tool me la da? ¿Con qué parámetros?&lt;/p&gt;

&lt;p&gt;En este caso: llama a &lt;code&gt;buscar_canciones(mood="fiesta")&lt;/code&gt;, analiza la energía con &lt;code&gt;analizar_energia(canciones=[...])&lt;/code&gt; para ordenarlas con un flow ascendente, y verifica la duración con &lt;code&gt;duracion_playlist(canciones=[...])&lt;/code&gt; para cubrir la hora completa. Si la duración no alcanza, puede volver a llamar &lt;code&gt;buscar_canciones&lt;/code&gt; con otros criterios.&lt;/p&gt;

&lt;p&gt;Tú no orquestaste nada. El modelo resolvió la secuencia en runtime.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota sobre modelos locales:&lt;/strong&gt; con &lt;code&gt;llama3.1:8b&lt;/code&gt;, el agente invoca los tools correctamente pero a veces ignora los resultados y alucina canciones que no están en tu biblioteca. Esto es una limitación del tamaño del modelo (8B parámetros), no del SDK. Con una sola herramienta (Capa 2) funciona bien; con múltiples tools el modelo se confunde más fácilmente. Si necesitas tool-calling más confiable, usa un modelo más grande (&lt;code&gt;llama3.1:70b&lt;/code&gt;) o un modelo cloud como Claude o GPT-4 (ver la sección "De tu laptop a la nube").&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Y si quieres entender cómo funciona por dentro, el código está ahí. El agent loop de Strands no es una caja negra. Es código abierto que puedes leer en &lt;a href="https://github.com/strands-agents/sdk-python" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capa 4: Memoria — el DJ te conoce
&lt;/h2&gt;

&lt;p&gt;Hay un problema con todo lo que hemos construido hasta ahora.&lt;/p&gt;

&lt;p&gt;Cada conversación empieza de cero.&lt;/p&gt;

&lt;p&gt;Le dices "me encanta el indie rock", cierras la conversación, vuelves al día siguiente, y el agente no tiene idea de quién eres. Es como ir a un bar donde el DJ cambia cada vez que parpadeas.&lt;/p&gt;

&lt;p&gt;Strands resuelve esto con session management. &lt;code&gt;FileSessionManager&lt;/code&gt; persiste el historial de conversación a disco como archivos JSON, organizados por sesión y agente. Cuando el agente se inicializa con un &lt;code&gt;session_id&lt;/code&gt; existente, carga los mensajes anteriores y el modelo los tiene como contexto.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.models.ollama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OllamaModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.session.file_session_manager&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FileSessionManager&lt;/span&gt;

&lt;span class="n"&gt;modelo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OllamaModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama3.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# El session manager guarda las conversaciones a disco
# session_id identifica al usuario, storage_dir es donde se guardan los archivos
&lt;/span&gt;&lt;span class="n"&gt;session_manager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FileSessionManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usuario-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;storage_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./sesiones&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;dj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;modelo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Eres un DJ y curador musical experto.
    Recuerdas los gustos del usuario entre conversaciones.
    Si el usuario ya te dijo qué le gusta, úsalo para personalizar tus playlists.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;buscar_canciones&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;analizar_energia&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duracion_playlist&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;session_manager&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;session_manager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Primera conversación
&lt;/span&gt;&lt;span class="nf"&gt;dj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Me encanta el indie rock y el shoegaze. No soporto el reggaetón.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ... tiempo después, otra conversación ...
&lt;/span&gt;
&lt;span class="c1"&gt;# El agente recuerda
&lt;/span&gt;&lt;span class="nf"&gt;dj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Armame algo para el viernes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# → Arma una playlist de indie rock y shoegaze, sin reggaetón
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pocas líneas y tu agente tiene continuidad.&lt;/p&gt;

&lt;p&gt;Y aquí viene lo interesante desde el punto de vista open source: &lt;code&gt;FileSessionManager&lt;/code&gt; es una implementación de la interfaz &lt;code&gt;SessionManager&lt;/code&gt;. Guarda sesiones a disco como archivos JSON organizados por sesión y agente. Pero si quieres guardar sesiones en Redis, en Postgres, o en S3, implementas la interfaz y listo. Ya existen session managers de la comunidad, como el de &lt;a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/strands-sdk-memory.html" rel="noopener noreferrer"&gt;AgentCore Memory&lt;/a&gt;. La arquitectura está diseñada para que la comunidad la extienda.&lt;/p&gt;

&lt;h2&gt;
  
  
  De tu laptop a la nube (en una línea)
&lt;/h2&gt;

&lt;p&gt;Todo lo que hemos construido corre localmente. Ollama en tu máquina, modelo en tu máquina, datos en tu máquina.&lt;/p&gt;

&lt;p&gt;Pero si quieres compartir tu agente con el mundo, necesitas algo más.&lt;/p&gt;

&lt;p&gt;Y aquí está lo elegante de la arquitectura: el SDK abstrae el model provider detrás de una interfaz común. Cambiar de proveedor es cambiar la instanciación del modelo — el resto del código no se entera.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Local con Ollama
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.models.ollama&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OllamaModel&lt;/span&gt;
&lt;span class="n"&gt;modelo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OllamaModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama3.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# En la nube con Amazon Bedrock
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;strands.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BedrockModel&lt;/span&gt;
&lt;span class="n"&gt;modelo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BedrockModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us.anthropic.claude-sonnet-4-20250514-v1:0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El resto del código del agente no cambia. Ni una línea. Los tools, el system prompt, el session manager: todo igual. Esto es posible porque Strands define una interfaz &lt;code&gt;Model&lt;/code&gt; que todos los providers implementan — &lt;code&gt;OllamaModel&lt;/code&gt;, &lt;code&gt;BedrockModel&lt;/code&gt;, &lt;code&gt;OpenAIModel&lt;/code&gt;, &lt;code&gt;AnthropicModel&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;Esa es la arquitectura en capas:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpvjbasy4dx5h5cke87y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpvjbasy4dx5h5cke87y.png" alt="Arquitectura en capas — tu código, el SDK, y el provider" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La capa open source es la constante. El proveedor de abajo es la variable. Ollama, Bedrock, OpenAI, Anthropic, Google Gemini, LiteLLM: todos son plugins que implementan la misma interfaz. Sin esa capa de abstracción, estás atado al SDK del vendor con el que empezaste.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que aprendiste
&lt;/h2&gt;

&lt;p&gt;Un agente de IA no es magia. Es un modelo + herramientas + un loop de razonamiento.&lt;/p&gt;

&lt;p&gt;Construimos un DJ de playlists capa por capa: primero un agente que solo habla, luego le dimos herramientas para buscar en una biblioteca real de canciones, después agregamos más tools y vimos cómo el modelo decide solo qué llamar y en qué orden, le dimos memoria para que recuerde tus gustos, y al final mostramos que el mismo código puede correr local o en la nube cambiando una línea.&lt;/p&gt;

&lt;p&gt;Todo con Python, &lt;a href="https://github.com/strands-agents/sdk-python" rel="noopener noreferrer"&gt;Strands Agents&lt;/a&gt; y &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;. Todo open source. Todo en tu laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;Si quieres ir más allá:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://strandsagents.com/" rel="noopener noreferrer"&gt;Documentación de Strands Agents&lt;/a&gt; — guías, ejemplos, y API reference&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/strands-agents/sdk-python" rel="noopener noreferrer"&gt;Repo de Strands en GitHub&lt;/a&gt; — el código fuente completo, licencia Apache 2.0&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/strands-agents/tools" rel="noopener noreferrer"&gt;Community tools&lt;/a&gt; — herramientas creadas por la comunidad que puedes usar y extender&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; — para correr modelos localmente&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El DJ sabe de música. ¿Qué vas a construir tú?&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Te resultó útil este artículo?&lt;/strong&gt; Compártelo con tu equipo o déjame saber en los comentarios qué agente te gustaría construir. Y si ya estás experimentando con Strands o con agentes de IA en general, me encantaría escuchar tu experiencia.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>opensource</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Escala Inteligente con Microservicios Serverless</title>
      <dc:creator>Hazel Saenz</dc:creator>
      <pubDate>Tue, 24 Mar 2026 17:57:16 +0000</pubDate>
      <link>https://dev.to/aws/escala-inteligente-con-microservicios-serverless-3lbe</link>
      <guid>https://dev.to/aws/escala-inteligente-con-microservicios-serverless-3lbe</guid>
      <description>&lt;p&gt;&lt;em&gt;Cuándo dividir, cómo conectar y qué patrones usar para no crear un monolito distribuido.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Hace un tiempo me tocó trabajar en un proyecto donde todo funcionaba. Un monolito. Un deploy, un repo, un equipo. Respondía rápido, los usuarios estaban contentos, el jefe estaba contento.&lt;/p&gt;

&lt;p&gt;Hasta que alguien en una conferencia dijo: &lt;em&gt;"los microservicios son el futuro"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Y miramos nuestro monolito y pensamos: &lt;em&gt;"esto no escala, necesitamos microservicios"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Tres meses después teníamos 15 servicios. Un request del usuario pasaba por 7 de ellos antes de responder. Si inventario iba lento, el checkout se caía. Si notificaciones fallaba, nadie sabía por qué el pedido se quedó a medias.&lt;/p&gt;

&lt;p&gt;Teníamos un &lt;strong&gt;monolito distribuido&lt;/strong&gt;. Los mismos problemas de antes, pero ahora con latencia de red y 12 dashboards que nadie entendía.&lt;/p&gt;

&lt;p&gt;La complejidad no desapareció. Solo cambió de forma.&lt;/p&gt;

&lt;p&gt;Si alguna vez sentiste que migrar a microservicios iba a resolver todos tus problemas y terminó creando nuevos... este artículo es para ti.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Realmente necesitas microservicios?
&lt;/h2&gt;

&lt;p&gt;Antes de hablar de &lt;em&gt;cómo&lt;/em&gt;, hablemos de &lt;em&gt;cuándo&lt;/em&gt;. Porque la decisión más inteligente a veces es &lt;strong&gt;no dividir&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Cuando empecé a investigar más sobre el tema, me di cuenta de que muchas migraciones a microservicios no nacen de un problema real. Nacen del hype. De escuchar que "Netflix usa microservicios" y asumir que nosotros también deberíamos.&lt;/p&gt;

&lt;p&gt;Pero Netflix llegó a microservicios después de años de crecimiento y con miles de ingenieros dedicados a esa transición. El contexto importa: lo que funciona para una empresa con esa escala no necesariamente aplica para todos los equipos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Señales reales vs. falsas alarmas
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;✅ Señales reales para dividir&lt;/th&gt;
&lt;th&gt;❌ Falsas alarmas&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Equipos se bloquean entre sí para hacer deploy&lt;/td&gt;
&lt;td&gt;"Netflix usa microservicios"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Un componente necesita escalar 100x más que el resto&lt;/td&gt;
&lt;td&gt;"Mi monolito es legacy"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dominios de negocio claramente independientes&lt;/td&gt;
&lt;td&gt;"Quiero usar tecnologías diferentes por servicio"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ciclos de release muy diferentes entre componentes&lt;/td&gt;
&lt;td&gt;"Los microservicios son más modernos"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Un fallo en un módulo tumba todo el sistema&lt;/td&gt;
&lt;td&gt;"Mi equipo de 3 personas necesita agilidad"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Si tu equipo es pequeño y todo cabe en un deploy, no necesitas microservicios. Necesitas un &lt;strong&gt;monolito bien estructurado&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Divide cuando el dolor es real: equipos que se bloquean, componentes que necesitan escalar de forma radicalmente diferente, dominios de negocio que evolucionan a ritmos distintos.&lt;/p&gt;

&lt;p&gt;No dividas porque suena moderno.&lt;/p&gt;

&lt;h3&gt;
  
  
  No es binario — hay un espectro
&lt;/h3&gt;

&lt;p&gt;Algo que me costó entender al principio es que no es "monolito o microservicios". Hay un espectro completo entre ambos:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F386umm3b5i9tzs304n95.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F386umm3b5i9tzs304n95.png" alt="monolito O Microservicios" width="800" height="126"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La mayoría de los sistemas no necesitan ir directo a microservicios.&lt;/p&gt;

&lt;p&gt;Empieza con un &lt;strong&gt;monolito modular&lt;/strong&gt;: un solo deploy, pero con límites claros entre dominios. Cuando un módulo necesite escalar o evolucionar independientemente, lo extraes.&lt;/p&gt;

&lt;p&gt;Eso es escalar inteligente: dividir cuando hay evidencia, no cuando hay hype.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serverless cambia las reglas
&lt;/h2&gt;

&lt;p&gt;Cuando trabajas con microservicios tradicionales, cada servicio necesita su servidor, su scaling, su service discovery. Es mucha carga operativa.&lt;/p&gt;

&lt;p&gt;Serverless cambia eso por completo:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Microservicios Tradicionales&lt;/th&gt;
&lt;th&gt;Microservicios Serverless&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tú manejas servidores por servicio&lt;/td&gt;
&lt;td&gt;Sin servidores que gestionar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tú configuras auto-scaling&lt;/td&gt;
&lt;td&gt;Escala automática por función&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pagas por servidor encendido&lt;/td&gt;
&lt;td&gt;Pagas por ejecución&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tú manejas service discovery&lt;/td&gt;
&lt;td&gt;Eventos y colas conectan servicios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes, Docker, load balancers&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://docs.aws.amazon.com/lambda?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Lambda&lt;/a&gt;, &lt;a href="https://docs.aws.amazon.com/apigateway?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;API Gateway&lt;/a&gt;, &lt;a href="https://docs.aws.amazon.com/eventbridge?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;EventBridge&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pero hay algo que quiero dejar claro porque es una confusión muy común:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Un microservicio serverless no es UNA Lambda.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Es la colección de Lambdas detrás de un API Gateway que resuelven un dominio de negocio. Pedidos, inventario, pagos, cada uno es un microservicio compuesto por varias funciones pequeñas y enfocadas.&lt;/p&gt;

&lt;p&gt;Si metes toda la lógica en una sola Lambda gigante, estás creando un monolito dentro de una función.&lt;/p&gt;

&lt;p&gt;La pregunta ya no es &lt;em&gt;"cómo escalo este servicio"&lt;/em&gt; sino &lt;em&gt;"cómo conecto estos servicios de forma inteligente"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Y ahí es donde entran los patrones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Los 3 patrones clave
&lt;/h2&gt;

&lt;p&gt;Después de haber pasado por varias migraciones (algunas exitosas, otras no tanto), identifiqué tres patrones que marcan la diferencia entre una arquitectura de microservicios que funciona y un monolito distribuido disfrazado.&lt;/p&gt;

&lt;p&gt;Los presento aquí a nivel conceptual. Cada uno tendrá su artículo dedicado con implementación práctica y código real.&lt;/p&gt;

&lt;h3&gt;
  
  
  Patrón 1: Comunicación desacoplada — eventos, no llamadas directas
&lt;/h3&gt;

&lt;p&gt;Este fue el error que más me costó entender.&lt;/p&gt;

&lt;p&gt;Cuando empezamos a dividir servicios, lo primero que hicimos fue conectar Lambdas en cadena. Lambda A llama a B, B llama a C. Parecía lógico.&lt;/p&gt;

&lt;p&gt;El problema:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0odggawr4p8vjbw8w454.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0odggawr4p8vjbw8w454.png" alt="anti patron 1" width="691" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si C falla, todo falla. Las latencias se suman. Estás recreando el monolito pero con más latencia.&lt;/p&gt;

&lt;p&gt;El patrón correcto es usar &lt;strong&gt;eventos asíncronos&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fklwvjn8qh8wj6cgkcvao.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fklwvjn8qh8wj6cgkcvao.png" alt="eventos asincronos" width="691" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tu microservicio responde al usuario y emite un evento. Los demás microservicios reaccionan al evento a su propio ritmo.&lt;/p&gt;

&lt;p&gt;Desacoplados. Independientes. Resilientes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/eventbridge/latest/userguide?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Amazon EventBridge&lt;/a&gt; actúa como un bus de eventos serverless que desacopla la lógica de enrutamiento de tus microservicios. &lt;a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Amazon SQS&lt;/a&gt; te da colas de mensajes para procesamiento asíncrono con garantías de entrega. Ambos son piezas fundamentales para este patrón.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;En el próximo artículo de la serie vamos a implementar este patrón paso a paso con EventBridge y SQS, incluyendo las trampas que te puedes encontrar.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Patrón 2: Cada servicio, su data
&lt;/h3&gt;

&lt;p&gt;Este es directo y no tiene vuelta:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcz1vocgio9mtgvlhbnjw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcz1vocgio9mtgvlhbnjw.png" alt="Antipatron 2 Cada servicio, su data" width="270" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si todos tus servicios leen y escriben en la misma tabla de DynamoDB, no tienes microservicios. Tienes un &lt;strong&gt;monolito disfrazado&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;El patrón correcto:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgw99ayqyvm6v9ocwk8vi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgw99ayqyvm6v9ocwk8vi.png" alt="Patron 2 Cada servicio, su datas" width="254" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cada servicio es dueño de su data. Si otro servicio necesita esa información, la pide por evento o por API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nunca por query directo a la base de datos ajena.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;¿Por qué? Porque si compartes la base de datos, cualquier cambio de schema puede romper múltiples servicios al mismo tiempo. Y ahí se fue tu independencia.&lt;/p&gt;

&lt;p&gt;Esto se conoce como &lt;em&gt;database per service&lt;/em&gt; y suena simple, pero tiene implicaciones importantes, como qué pasa cuando una transacción involucra múltiples servicios (spoiler: necesitas el patrón Saga).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;En el tercer artículo de la serie vamos a profundizar en este patrón y en cómo manejar transacciones distribuidas con Sagas.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Patrón 3: Observabilidad desde el día 1
&lt;/h3&gt;

&lt;p&gt;En un monolito, un error aparece en un log. En microservicios, un error puede estar en cualquiera de 10 servicios.&lt;/p&gt;

&lt;p&gt;Sin observabilidad, estás debuggeando a ciegas. Y te lo digo por experiencia: no hay nada más frustrante que saber que algo falló y no poder encontrar dónde.&lt;/p&gt;

&lt;p&gt;Los tres pilares que necesitas desde el día 1:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Logs estructurados con correlation ID&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota importante:&lt;/strong&gt; Cada request que entra a tu sistema debe llevar un ID único que viaje por todos los servicios. Cuando algo falla, buscas ese ID y ves el camino completo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://docs.powertools.aws.dev/lambda/python/latest/" rel="noopener noreferrer"&gt;Lambda Powertools&lt;/a&gt; te lo da casi gratis, inyecta correlation IDs, estructura tus logs en JSON y agrega contexto automáticamente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Traces distribuidos&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/xray/latest/devguide?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;AWS X-Ray&lt;/a&gt; y &lt;a href="https://aws.amazon.com/otel?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;AWS Distro for OpenTelemetry (ADOT)&lt;/a&gt; te permiten ver el viaje completo de cada request a través de todos los servicios. Puedes identificar exactamente dónde está el bottleneck.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fw5jubypqpbslevq282.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fw5jubypqpbslevq282.png" alt="Observabilidad" width="800" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nota importante:&lt;/strong&gt; AWS está convergiendo hacia OpenTelemetry como estándar de instrumentación. ADOT te da una distribución optimizada para AWS que envía traces y métricas a X-Ray, CloudWatch y otros destinos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Métricas de negocio&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No solo "cuántos errores 500" sino "cuántos pedidos se perdieron por minuto". Las métricas técnicas te dicen que algo falló. Las métricas de negocio te dicen cuánto te costó.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Si no puedes ver qué pasa, no puedes arreglar lo que falla. En el cuarto artículo de la serie vamos a implementar observabilidad completa con Lambda Powertools, X-Ray y métricas custom.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  El framework de decisión
&lt;/h2&gt;

&lt;p&gt;Después de todo lo que vimos, quiero dejarte algo práctico. Un framework que puedes usar la próxima vez que alguien diga "necesitamos microservicios":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgxm3uk3xsnjsmd80gh7z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgxm3uk3xsnjsmd80gh7z.png" alt="framework de decision" width="800" height="647"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No empieces por la tecnología. Empieza por las preguntas.&lt;/p&gt;

&lt;p&gt;Si la respuesta a todo es no, quédate con tu monolito. Si es sí, extrae con estrategia.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que quiero que te lleves
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;🧠 &lt;strong&gt;Decide con evidencia, no con hype&lt;/strong&gt;: Microservicios no son el default. Son una herramienta para problemas específicos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;📐 &lt;strong&gt;Empieza modular, extrae después&lt;/strong&gt;: Monolito modular → macro-servicios → microservicios. En ese orden.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🔌 &lt;strong&gt;Eventos sobre llamadas directas&lt;/strong&gt;: Desacopla con EventBridge/SQS. Nunca encadenes Lambdas síncronamente.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🗄️ &lt;strong&gt;Cada servicio, su data&lt;/strong&gt;: Base de datos compartida = monolito disfrazado.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;👀 &lt;strong&gt;Observabilidad no es opcional&lt;/strong&gt;: Correlation IDs, X-Ray/OpenTelemetry, métricas de negocio. Desde el día 1.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;Este artículo es el mapa conceptual. Los siguientes artículos de la serie van a profundizar en cada patrón con implementación práctica y código real:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Comunicación desacoplada con EventBridge y SQS&lt;/strong&gt;: Implementación paso a paso, trampas comunes y demo funcional&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database per service y el patrón Saga&lt;/strong&gt;: Cómo manejar transacciones distribuidas con acciones compensatorias&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observabilidad en microservicios serverless&lt;/strong&gt;: Lambda Powertools, X-Ray/OpenTelemetry y métricas de negocio en acción&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Escalar inteligente no es tener más servicios. Es tomar mejores decisiones."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Recursos
&lt;/h2&gt;

&lt;p&gt;Si quieres profundizar más, estos son los recursos oficiales de AWS que recomiendo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/whitepapers/latest/microservices-on-aws/microservices-on-aws.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Implementing Microservices on AWS&lt;/a&gt; — Whitepaper completo sobre patrones API-driven, event-driven y data streaming&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/whitepapers/latest/microservices-on-aws/microservices-on-serverless-technologies.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Microservices on Serverless Technologies&lt;/a&gt; — Arquitecturas serverless con Lambda, API Gateway y Fargate&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/modernization-integrating-microservices/introduction.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Integrating Microservices by Using AWS Serverless Services&lt;/a&gt; — Guía prescriptiva sobre comunicación síncrona y asíncrona entre microservicios&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/introduction.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Cloud Design Patterns, Architectures, and Implementations&lt;/a&gt; — Patrones como Saga, circuit breaker, event sourcing y strangler fig aplicados con servicios AWS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;¿Te resultó útil este artículo?&lt;/strong&gt; Compártelo con tu equipo. Y si ya pasaste por una migración a microservicios exitosa o no me encantaría escuchar tu experiencia.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>microservices</category>
      <category>architecture</category>
      <category>aws</category>
    </item>
    <item>
      <title>AWS Lambda Durable Functions + IA: el combo que no sabías que necesitabas</title>
      <dc:creator>Hazel Saenz</dc:creator>
      <pubDate>Wed, 18 Mar 2026 16:56:38 +0000</pubDate>
      <link>https://dev.to/aws/aws-lambda-durable-functions-ia-el-combo-que-no-sabias-que-necesitabas-3eoc</link>
      <guid>https://dev.to/aws/aws-lambda-durable-functions-ia-el-combo-que-no-sabias-que-necesitabas-3eoc</guid>
      <description>&lt;p&gt;&lt;em&gt;Cómo instalar y usar el nuevo Kiro Power para Lambda Durable Functions paso a paso.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;En el &lt;a href="https://dev.to/aws/lambda-durable-functions-mata-a-step-functions-o-no-2p6c"&gt;artículo anterior&lt;/a&gt; vimos que Lambda Durable Functions y Step Functions no compiten. Son dos filosofías diferentes para resolver el mismo problema: orquestación de workflows.&lt;/p&gt;

&lt;p&gt;Step Functions orquesta visualmente. Durable Functions orquesta en código.&lt;/p&gt;

&lt;p&gt;Y prometí que en este artículo íbamos a pasar de la teoría a la práctica.&lt;/p&gt;

&lt;p&gt;Pero hay un detalle que no mencioné:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Construir tu primera Durable Function no tiene que ser complicado.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Porque desde marzo de 2026, existe un &lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/03/lambda-durable-kiro-power/?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Kiro Power&lt;/a&gt; que te guía paso a paso. Con IA. Dentro de tu IDE.&lt;/p&gt;

&lt;p&gt;Y eso cambia todo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Espera... ¿qué es Kiro?
&lt;/h2&gt;

&lt;p&gt;Si es la primera vez que escuchas el nombre, no te preocupes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kiro.dev?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt; es un IDE creado por AWS, potenciado con IA. Piensa en él como tu editor de código, pero con un agente de IA integrado que entiende lo que estás construyendo y te ayuda a hacerlo mejor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffakj0k6wmsgk5s4271u3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffakj0k6wmsgk5s4271u3.png" alt="Kiro IDE mostrando el agente de IA en acción" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No es un chatbot que pega código de internet. No es un autocompletado glorificado.&lt;/p&gt;

&lt;p&gt;Es un agente que &lt;strong&gt;entiende el contexto&lt;/strong&gt; de tu proyecto, sigue buenas prácticas, y te guía mientras escribes código.&lt;/p&gt;

&lt;p&gt;Puedes usarlo para escribir código, debuggear, hacer deploy, y mucho más. Si quieres conocerlo a fondo, te recomiendo esta &lt;a href="https://builder.aws.com/content/34X2JujaGkTJed5N2KeMYt1Mz9m/como-comenzar-con-kiro-tu-guia-de-primeros-pasos?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;guía de primeros pasos con Kiro&lt;/a&gt;. Pero lo que lo hace especial para este artículo es algo llamado &lt;strong&gt;&lt;a href="https://kiro.dev/powers?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Powers&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué es un Kiro Power?
&lt;/h2&gt;

&lt;p&gt;Un Power es como darle superpoderes especializados al agente de IA de Kiro.&lt;/p&gt;

&lt;p&gt;Sin un Power, el agente sabe de todo un poco. Con un Power instalado, el agente se convierte en &lt;strong&gt;experto&lt;/strong&gt; en un tema específico.&lt;/p&gt;

&lt;p&gt;Piensa en esto: si le pides a un asistente genérico que te ayude a construir una Durable Function, probablemente te dé una respuesta genérica. Puede que funcione. Puede que no.&lt;/p&gt;

&lt;p&gt;Pero si ese asistente tiene cargado un Power de Lambda Durable Functions, se convierte en un experto que &lt;strong&gt;sabe&lt;/strong&gt; las reglas, los patrones, y las trampas comunes. Y carga esa información dinámicamente según lo que estés haciendo.&lt;/p&gt;

&lt;h2&gt;
  
  
  El Lambda Durable Functions Power
&lt;/h2&gt;

&lt;p&gt;El 5 de marzo de 2026, AWS lanzó el &lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/03/lambda-durable-kiro-power/?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Kiro Power para Lambda Durable Functions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Incluye guía especializada para todo el ciclo de desarrollo: desde el setup del proyecto y las reglas del replay model, pasando por steps, waits, callbacks, ejecución en paralelo, manejo de errores, testing local, hasta deploy con CDK, SAM, o CloudFormation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6g0u7skigd98cii83jhf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6g0u7skigd98cii83jhf.png" alt="Diagrama de lo que incluye el Power — replay model, steps, waits, error handling, testing, deployment" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No tienes que leer toda la documentación antes de empezar. El Power te da lo que necesitas, cuando lo necesitas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instalando el Power
&lt;/h2&gt;

&lt;p&gt;Esto es la parte más fácil. Literalmente un clic.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Abre Kiro&lt;/li&gt;
&lt;li&gt;Ve al panel de Powers (en la barra lateral)&lt;/li&gt;
&lt;li&gt;En la sección &lt;strong&gt;Available&lt;/strong&gt;, busca "Lambda Durable Functions"&lt;/li&gt;
&lt;li&gt;Haz clic en &lt;strong&gt;Install&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Eso es todo.&lt;/p&gt;

&lt;p&gt;No hay configuración adicional. No hay dependencias que instalar manualmente para el Power. Una vez instalado, el agente de Kiro ya tiene acceso a toda la guía especializada.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; No todos los servicios de AWS tienen un Power dedicado. Los Powers son módulos especializados que se van agregando con el tiempo. Puedes explorar los disponibles en el panel de Powers de Kiro.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ahora sí, vamos a construir algo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisitos
&lt;/h2&gt;

&lt;p&gt;Antes de escribir código, necesitas tener algunas herramientas instaladas. Los siguientes comandos te ayudan a verificar si ya las tienes. Si alguno falla, sigue el link de documentación correspondiente para instalarlo.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AWS CLI&lt;/strong&gt; instalado (versión 2.33.22 o superior) y &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;configurado con tus credenciales&lt;/a&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--version&lt;/span&gt;
aws sts get-caller-identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js 22+&lt;/a&gt;&lt;/strong&gt; instalado (para este tutorial usaremos TypeScript):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AWS CDK&lt;/strong&gt; instalado globalmente (versión 2.237.1 o superior). Este se instala a nivel global en tu máquina, no dentro del proyecto:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; aws-cdk
cdk &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si prefieres Python, &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Lambda Durable Functions&lt;/a&gt; también soporta Python 3.11+. Pero para este tutorial vamos con TypeScript porque es el lenguaje que más uso y el que me resulta más natural para explicar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creando tu primer proyecto
&lt;/h2&gt;

&lt;p&gt;Abre Kiro y crea una carpeta nueva para tu proyecto. Luego, en el chat del agente, puedes pedirle directamente:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Crea un proyecto de Lambda Durable Function con TypeScript y CDK"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;El agente, con el Power instalado, va a:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inicializar el proyecto con la estructura correcta&lt;/li&gt;
&lt;li&gt;Instalar las dependencias necesarias&lt;/li&gt;
&lt;li&gt;Configurar TypeScript&lt;/li&gt;
&lt;li&gt;Crear los archivos base&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pero para que entiendas qué está pasando, vamos a hacerlo paso a paso.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inicializar el proyecto
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;image-processor
&lt;span class="nb"&gt;cd &lt;/span&gt;image-processor
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Instalar dependencias
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @aws/durable-execution-sdk-js
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @aws/durable-execution-sdk-js-testing typescript @types/node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ese primer paquete es el &lt;a href="https://github.com/aws/aws-durable-execution-sdk-js?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;SDK de Durable Execution&lt;/a&gt;. Es el core de las Durable Functions: lo que hace posible que tu función guarde checkpoints y sobreviva a fallos.&lt;/p&gt;

&lt;p&gt;El segundo paquete (&lt;code&gt;-testing&lt;/code&gt;) es para testear localmente sin necesidad de deployar a AWS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Estructura del proyecto
&lt;/h3&gt;

&lt;p&gt;Kiro va a generar esta estructura por ti cuando le pidas crear el proyecto. Pero si prefieres hacerlo manual, así es como debería verse:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image-processor/
├── src/
│   └── handler.ts          # Tu función durable
├── tests/
│   └── handler.test.ts     # Tests locales
├── infrastructure/
│   └── stack.ts             # CDK stack para deploy
├── tsconfig.json
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple. Sin carpetas innecesarias. Sin archivos que no vas a usar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tu primera Durable Function (generada por el Power)
&lt;/h2&gt;

&lt;p&gt;Ahora viene la parte divertida.&lt;/p&gt;

&lt;p&gt;¿Recuerdas el ejemplo del &lt;a href="https://dev.to/aws/lambda-durable-functions-mata-a-step-functions-o-no-2p6c"&gt;artículo anterior&lt;/a&gt;? Un usuario sube una foto y necesitas: validar que sea una imagen válida, redimensionar a diferentes tamaños, aplicar marca de agua, generar thumbnails, y guardar en S3.&lt;/p&gt;

&lt;p&gt;Abre el chat de Kiro y escríbele algo como:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Crea una Durable Function que procese imágenes: validar el archivo, redimensionar, aplicar marca de agua, generar thumbnails y guardar en S3"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;El agente, con el Power instalado, &lt;strong&gt;genera el código por ti&lt;/strong&gt;. No copia y pega de internet. Entiende las reglas del replay model, sabe qué va dentro de un step y qué no, y estructura el workflow siguiendo las buenas prácticas del SDK.&lt;/p&gt;

&lt;p&gt;Esto es lo que el Power generó para mí cuando le pedí exactamente eso:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Floq30by0lgdpwou1b7nv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Floq30by0lgdpwou1b7nv.png" alt="Screenshot del agente de Kiro generando el código de la Durable Function en el chat" width="800" height="464"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withDurableExecution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ImageEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DurableContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputBucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputBucket&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputPrefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputPrefix&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;processed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.[^&lt;/span&gt;&lt;span class="sr"&gt;.&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.[^&lt;/span&gt;&lt;span class="sr"&gt;.&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;)?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 1: Validate the file&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;validate-file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`File not found: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transformToByteArray&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tiff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Unsupported image format: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Supported: jpeg, png, webp, tiff`&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;File exceeds 50MB limit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;File validated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 2: Resize the image&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resizedBase64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize-image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxWidth&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxHeight&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inside&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;withoutEnlargement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resizedKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;outputPrefix&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-resized&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;outputBucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resizedKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;ContentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`image/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}));&lt;/span&gt;

      &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Image resized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resizedKey&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;resized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;retryStrategy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ver codigo completo: &lt;a href="https://github.com/hsaenzG/durable-funtions-demo/blob/main/src/handler.ts" rel="noopener noreferrer"&gt;https://github.com/hsaenzG/durable-funtions-demo/blob/main/src/handler.ts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Parece código normal, ¿verdad? Y lo es. Pero con superpoderes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Qué está pasando aquí
&lt;/h3&gt;

&lt;p&gt;Cada &lt;code&gt;context.step()&lt;/code&gt; es un &lt;strong&gt;checkpoint&lt;/strong&gt;. Si tu función falla aplicando la marca de agua (paso 3), cuando se reintente, &lt;strong&gt;no va a volver a validar ni redimensionar&lt;/strong&gt;. Salta directamente al paso 3 con los resultados que ya tenía guardados.&lt;/p&gt;

&lt;p&gt;Eso es el replay model en acción. Y para un workflow de procesamiento de imágenes, donde cada paso puede tardar segundos o minutos, no repetir trabajo es la diferencia entre una buena experiencia y una pesadilla.&lt;/p&gt;

&lt;p&gt;Fíjate también en &lt;code&gt;context.logger&lt;/code&gt; en lugar de &lt;code&gt;console.log&lt;/code&gt;: es &lt;strong&gt;replay-aware&lt;/strong&gt;. No duplica logs cuando la función se reintenta.&lt;/p&gt;

&lt;h2&gt;
  
  
  El Kiro Power y la regla de oro del Replay Model
&lt;/h2&gt;

&lt;p&gt;Aquí es donde el Kiro Power realmente brilla. Porque hay una regla que la mayoría no conocemos hasta que algo se rompe:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Todo código no determinístico DEBE ir dentro de un step.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Y el Power te avisa cuando estás a punto de violar esa regla. No tienes que memorizar todos los casos. El agente los detecta y te sugiere la corrección antes de que sea un problema.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1tzve877ywwkjmlhxyya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1tzve877ywwkjmlhxyya.png" alt="creenshot del agente de Kiro detectando una violación del replay model y sugiriendo la corrección" width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pero, ¿por qué importa tanto esta regla?&lt;/p&gt;

&lt;p&gt;Cuando una Durable Function se reintenta (replay), ejecuta todo el código desde el principio. Los steps que ya completaron se saltan (usa el resultado guardado). Pero el código que está &lt;strong&gt;fuera&lt;/strong&gt; de los steps se ejecuta de nuevo.&lt;/p&gt;

&lt;p&gt;Si ese código da un resultado diferente en el replay... las cosas se rompen.&lt;/p&gt;

&lt;p&gt;¿Qué es código no determinístico? Cualquier cosa que puede dar un resultado diferente cada vez:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Date.now()&lt;/code&gt; → diferente cada vez&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Math.random()&lt;/code&gt; → diferente cada vez&lt;/li&gt;
&lt;li&gt;Llamadas a APIs externas → pueden devolver datos diferentes&lt;/li&gt;
&lt;li&gt;Consultas a bases de datos → los datos pueden haber cambiado&lt;/li&gt;
&lt;li&gt;Generación de UUIDs → diferente cada vez&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Veamos un ejemplo concreto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ MAL: Date.now() fuera de un step&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Diferente en cada replay&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;save&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ BIEN: Date.now() dentro del step&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;save-with-timestamp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Se guarda con el checkpoint&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sin el Power, descubrir este tipo de errores es prueba y error. Con el Power, el agente te lo señala mientras escribes.&lt;/p&gt;

&lt;p&gt;Además, puedes instalar el plugin de ESLint para Durable Functions que atrapa estos errores en tiempo de desarrollo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @aws/durable-execution-sdk-js-eslint-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testeando localmente
&lt;/h2&gt;

&lt;p&gt;No necesitas hacer deploy para probar tu función. El SDK incluye un test runner local.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LocalDurableTestRunner&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws/durable-execution-sdk-js-testing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../src/handler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Image Processing Workflow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should process an image successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LocalDurableTestRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;skipTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-images-bucket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uploads/photo.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Verificar que el procesamiento se completó&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalKey&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uploads/photo.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Verificar que los steps se ejecutaron&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOperations&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validateStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;validate-image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resizeStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize-image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;watermarkStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apply-watermark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thumbnailStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;generate-thumbnails&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saveStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;save-to-s3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validateStep&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeDefined&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resizeStep&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeDefined&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;watermarkStep&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeDefined&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thumbnailStep&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeDefined&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;saveStep&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeDefined&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El &lt;code&gt;skipTime: true&lt;/code&gt; es clave: hace que los &lt;code&gt;wait&lt;/code&gt; se salten instantáneamente en los tests.&lt;/p&gt;

&lt;p&gt;Y fíjate que buscamos los steps &lt;strong&gt;por nombre&lt;/strong&gt;, no por índice. Eso es una buena práctica que el Power te recuerda constantemente: siempre nombra tus steps de forma descriptiva.&lt;/p&gt;

&lt;h2&gt;
  
  
  Haciendo deploy con CDK
&lt;/h2&gt;

&lt;p&gt;Ya tienes tu función funcionando localmente. Ahora toca subirla a AWS.&lt;/p&gt;

&lt;p&gt;Para esto usamos &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/home.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;AWS CDK&lt;/a&gt;, que te permite definir tu infraestructura en código (TypeScript, en nuestro caso).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-lambda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;logs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-logs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-iam&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DurableFunctionStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Log group explícito para mejor control&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LogGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ImageProcessingLogs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;logGroupName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/aws/lambda/imageProcessing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;retention&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RetentionDays&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ONE_WEEK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;removalPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RemovalPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DESTROY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// La función durable&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ImageProcessing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODEJS_24_X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handler.handler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;logGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;logGroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;durableConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;executionTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;retentionPeriod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Política de permisos para durable execution&lt;/span&gt;
    &lt;span class="nx"&gt;imageFunction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;addManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAwsManagedPolicyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;service-role/AWSLambdaBasicDurableExecutionRolePolicy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Crear alias para invocación&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;imageFunction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentVersion&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alias&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ProdAlias&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;aliasName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CfnOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FunctionAliasArn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;functionArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Lo que importa aquí
&lt;/h3&gt;

&lt;p&gt;Hay tres cosas que no puedes olvidar al hacer deploy de una Durable Function:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;durableConfig&lt;/code&gt;&lt;/strong&gt;: Sin esto, tu función es una Lambda normal. El &lt;code&gt;executionTimeout&lt;/code&gt; define cuánto tiempo máximo puede correr tu workflow (hasta 1 año). El &lt;code&gt;retentionPeriod&lt;/code&gt; define cuánto tiempo se guardan los datos de ejecución.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;AWSLambdaBasicDurableExecutionRolePolicy&lt;/code&gt;&lt;/strong&gt;: Esta política le da a tu función permisos para hacer checkpoints (&lt;code&gt;lambda:CheckpointDurableExecution&lt;/code&gt;) y leer el estado de ejecución (&lt;code&gt;lambda:GetDurableExecutionState&lt;/code&gt;). Sin ella, los steps no pueden guardar su progreso.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ARN calificado&lt;/strong&gt;: Las Durable Functions &lt;strong&gt;requieren&lt;/strong&gt; un &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;ARN&lt;/a&gt; calificado para ser invocadas. Un ARN (Amazon Resource Name) es el identificador único de un recurso en AWS. "Calificado" significa que incluye la versión o alias de la función. No puedes invocar una Durable Function con el ARN sin calificar.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ✅ Correcto: con alias&lt;/span&gt;
aws lambda invoke &lt;span class="nt"&gt;--function-name&lt;/span&gt; &lt;span class="s1"&gt;'MiStack-MiFuncion-abc123:prod'&lt;/span&gt; response.json

&lt;span class="c"&gt;# ✅ Correcto: con versión&lt;/span&gt;
aws lambda invoke &lt;span class="nt"&gt;--function-name&lt;/span&gt; &lt;span class="s1"&gt;'MiStack-MiFuncion-abc123:1'&lt;/span&gt; response.json

&lt;span class="c"&gt;# ❌ Incorrecto: sin calificar — va a fallar&lt;/span&gt;
aws lambda invoke &lt;span class="nt"&gt;--function-name&lt;/span&gt; MiStack-MiFuncion-abc123 response.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; CDK genera nombres de recursos con un sufijo aleatorio (ej: &lt;code&gt;ImageProcessorStack-ImageProcessor5D0B0257-lBARjqDaXdWK&lt;/code&gt;). Puedes encontrar el nombre exacto de tu función en el output del &lt;code&gt;cdk deploy&lt;/code&gt; o en la consola de AWS Lambda.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Deploy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Compilar TypeScript&lt;/span&gt;
npx tsc

&lt;span class="c"&gt;# Deploy con CDK&lt;/span&gt;
cdk deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CDK se encarga de crear todo: la función, el rol de IAM, los permisos, el log group, la versión y el alias.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnds21mbsr10rh5lkkvow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnds21mbsr10rh5lkkvow.png" alt="IScreenshot del deploy con CDK ejecutándose en la terminal" width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Invocando tu función
&lt;/h2&gt;

&lt;p&gt;Ya está en la nube. Ahora vamos a probarla.&lt;/p&gt;

&lt;p&gt;Primero, sube una imagen de prueba al bucket de S3 que CDK creó:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;tu-imagen.jpg &lt;span class="se"&gt;\&lt;/span&gt;
  s3://TU-BUCKET-S3/photos/test.jpg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--profile&lt;/span&gt; TU-PERFIL &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; El nombre del bucket lo genera CDK automáticamente (ej: &lt;code&gt;imageprocessorstack-imagebucket97210811-x5j3e61lysxh&lt;/code&gt;). Puedes encontrarlo en el output del &lt;code&gt;cdk deploy&lt;/code&gt; o en la consola de S3.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ahora sí, invoca la función:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws lambda invoke &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--function-name&lt;/span&gt; &lt;span class="s1"&gt;'TU-NOMBRE-DE-FUNCION:prod'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--invocation-type&lt;/span&gt; Event &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--durable-execution-name&lt;/span&gt; &lt;span class="s2"&gt;"test-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--payload&lt;/span&gt; &lt;span class="s1"&gt;'{"bucket":"TU-BUCKET-S3","key":"photos/test.jpg"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cli-binary-format&lt;/span&gt; raw-in-base64-out &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--profile&lt;/span&gt; TU-PERFIL &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="se"&gt;\&lt;/span&gt;
  response.json

&lt;span class="nb"&gt;cat &lt;/span&gt;response.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Reemplaza &lt;code&gt;TU-NOMBRE-DE-FUNCION&lt;/code&gt;, &lt;code&gt;TU-BUCKET-S3&lt;/code&gt; y &lt;code&gt;TU-PERFIL&lt;/code&gt; con los valores reales de tu entorno. Revisa los outputs del &lt;code&gt;cdk deploy&lt;/code&gt; para encontrarlos.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0nkoicfiymikbsnby8we.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0nkoicfiymikbsnby8we.png" alt="Screenshot de la respuesta JSON en la terminal después de invocar la función" width="800" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Algunos detalles sobre estos parámetros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--invocation-type Event&lt;/code&gt;&lt;/strong&gt;: Invoca la función de forma asíncrona. Para workflows largos como procesamiento de imágenes, esto es lo recomendado. Si quieres esperar la respuesta, usa &lt;code&gt;RequestResponse&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--durable-execution-name "test-$(date +%s)"&lt;/code&gt;&lt;/strong&gt;: Genera un nombre único por ejecución usando un timestamp. Esto garantiza &lt;strong&gt;idempotencia&lt;/strong&gt;: si invocas con el mismo nombre dos veces, la segunda no crea una ejecución nueva.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--profile&lt;/code&gt; y &lt;code&gt;--region&lt;/code&gt;&lt;/strong&gt;: Si tienes múltiples perfiles de AWS configurados, no olvides especificarlos.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Monitoreando tus ejecuciones
&lt;/h2&gt;

&lt;p&gt;Ya invocaste tu función. ¿Y ahora? ¿Cómo sabes si funcionó?&lt;/p&gt;

&lt;p&gt;No estás a ciegas. La consola de AWS Lambda tiene una interfaz dedicada para Durable Functions donde puedes ver todo lo que está pasando.&lt;/p&gt;

&lt;p&gt;Ve a tu función en la consola y busca la pestaña &lt;strong&gt;Durable executions&lt;/strong&gt;. Ahí vas a encontrar la lista de todas las ejecuciones con su estado: running, completed, failed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1w5zhcbyzwd0l8gko8n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1w5zhcbyzwd0l8gko8n.png" alt="Screenshot de la consola de AWS Lambda mostrando la lista de ejecuciones durables con sus estados" width="800" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Haz clic en cualquier ejecución y vas a ver el detalle completo: qué steps se completaron, cuáles fallaron, cuánto tardó cada uno, y los datos que se guardaron en cada checkpoint.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9c9g5im3wvekd34m1ol2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9c9g5im3wvekd34m1ol2.png" alt="Screenshot del detalle de una ejecución durable mostrando los steps completados y sus resultados" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esto es oro para debuggear. Si algo falló en el paso 3, puedes ver exactamente qué datos tenía en ese momento y qué error se produjo. Sin adivinar. Sin buscar en CloudWatch a ciegas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué esto importa si estás empezando
&lt;/h2&gt;

&lt;p&gt;Sin el Power, el camino típico es: leer toda la documentación, tropezar con el replay model, configurar el proyecto a mano, y descubrir errores de deploy por prueba y error.&lt;/p&gt;

&lt;p&gt;Con el Power, el agente te guía en cada paso. No elimina la necesidad de entender los conceptos (eso sigue siendo importante). Pero reduce drásticamente el tiempo entre "quiero aprender Durable Functions" y "tengo una funcionando en producción".&lt;/p&gt;

&lt;h2&gt;
  
  
  Limpieza de recursos
&lt;/h2&gt;

&lt;p&gt;Si solo estás probando, recuerda eliminar los recursos para evitar cargos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cdk destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto elimina la función Lambda, el rol de IAM, el log group, y todos los recursos asociados. CDK se encarga de todo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que aprendiste
&lt;/h2&gt;

&lt;p&gt;Instalaste un Kiro Power con un clic, creaste un proyecto desde cero, dejaste que el agente generara tu primera Durable Function con el mismo ejemplo de procesamiento de imágenes del artículo anterior, entendiste la regla de oro del replay model, testeaste localmente sin deploy, y subiste todo a AWS con CDK.&lt;/p&gt;

&lt;p&gt;De cero a producción. Con IA guiándote en cada paso.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;Ya tienes el workflow de procesamiento de imágenes funcionando. Pero lo construimos en el camino feliz: todo sale bien, nada falla.&lt;/p&gt;

&lt;p&gt;En el próximo artículo vamos a &lt;strong&gt;romper cosas a propósito&lt;/strong&gt;. Vamos a ver cómo los checkpoints automáticos mantienen tu progreso incluso cuando algo truena, y cómo el replay model recupera la ejecución exactamente donde quedó.&lt;/p&gt;

&lt;p&gt;Código real. Fallos reales. Recuperación real.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Te resultó útil este artículo?&lt;/strong&gt; Compártelo con tu equipo o déjame saber en los comentarios qué otros temas de serverless te gustaría que cubriera. Y si ya instalaste el Kiro Power y estás construyendo tu primera Durable Function, me encantaría escuchar tu experiencia.&lt;/p&gt;

</description>
      <category>lambda</category>
      <category>genai</category>
      <category>devtools</category>
      <category>español</category>
    </item>
    <item>
      <title>AWS Lambda Durable Functions + IA: el combo que no sabías que necesitabas</title>
      <dc:creator>Hazel Saenz</dc:creator>
      <pubDate>Wed, 18 Mar 2026 16:56:38 +0000</pubDate>
      <link>https://dev.to/aws/aws-lambda-durable-functions-ia-el-combo-que-no-sabias-que-necesitabas-3lh2</link>
      <guid>https://dev.to/aws/aws-lambda-durable-functions-ia-el-combo-que-no-sabias-que-necesitabas-3lh2</guid>
      <description>&lt;p&gt;&lt;em&gt;Cómo instalar y usar el nuevo Kiro Power para Lambda Durable Functions paso a paso.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;En el &lt;a href="https://dev.to/aws/lambda-durable-functions-mata-a-step-functions-o-no-2p6c"&gt;artículo anterior&lt;/a&gt; vimos que Lambda Durable Functions y Step Functions no compiten. Son dos filosofías diferentes para resolver el mismo problema: orquestación de workflows.&lt;/p&gt;

&lt;p&gt;Step Functions orquesta visualmente. Durable Functions orquesta en código.&lt;/p&gt;

&lt;p&gt;Y prometí que en este artículo íbamos a pasar de la teoría a la práctica.&lt;/p&gt;

&lt;p&gt;Pero hay un detalle que no mencioné:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Construir tu primera Durable Function no tiene que ser complicado.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Porque desde marzo de 2026, existe un &lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/03/lambda-durable-kiro-power/?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Kiro Power&lt;/a&gt; que te guía paso a paso. Con IA. Dentro de tu IDE.&lt;/p&gt;

&lt;p&gt;Y eso cambia todo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Espera... ¿qué es Kiro?
&lt;/h2&gt;

&lt;p&gt;Si es la primera vez que escuchas el nombre, no te preocupes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kiro.dev?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt; es un IDE creado por AWS, potenciado con IA. Piensa en él como tu editor de código, pero con un agente de IA integrado que entiende lo que estás construyendo y te ayuda a hacerlo mejor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffakj0k6wmsgk5s4271u3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffakj0k6wmsgk5s4271u3.png" alt="Kiro IDE mostrando el agente de IA en acción" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No es un chatbot que pega código de internet. No es un autocompletado glorificado.&lt;/p&gt;

&lt;p&gt;Es un agente que &lt;strong&gt;entiende el contexto&lt;/strong&gt; de tu proyecto, sigue buenas prácticas, y te guía mientras escribes código.&lt;/p&gt;

&lt;p&gt;Puedes usarlo para escribir código, debuggear, hacer deploy, y mucho más. Si quieres conocerlo a fondo, te recomiendo esta &lt;a href="https://builder.aws.com/content/34X2JujaGkTJed5N2KeMYt1Mz9m/como-comenzar-con-kiro-tu-guia-de-primeros-pasos?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;guía de primeros pasos con Kiro&lt;/a&gt;. Pero lo que lo hace especial para este artículo es algo llamado &lt;strong&gt;&lt;a href="https://kiro.dev/powers?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Powers&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué es un Kiro Power?
&lt;/h2&gt;

&lt;p&gt;Un Power es como darle superpoderes especializados al agente de IA de Kiro.&lt;/p&gt;

&lt;p&gt;Sin un Power, el agente sabe de todo un poco. Con un Power instalado, el agente se convierte en &lt;strong&gt;experto&lt;/strong&gt; en un tema específico.&lt;/p&gt;

&lt;p&gt;Piensa en esto: si le pides a un asistente genérico que te ayude a construir una Durable Function, probablemente te dé una respuesta genérica. Puede que funcione. Puede que no.&lt;/p&gt;

&lt;p&gt;Pero si ese asistente tiene cargado un Power de Lambda Durable Functions, se convierte en un experto que &lt;strong&gt;sabe&lt;/strong&gt; las reglas, los patrones, y las trampas comunes. Y carga esa información dinámicamente según lo que estés haciendo.&lt;/p&gt;

&lt;h2&gt;
  
  
  El Lambda Durable Functions Power
&lt;/h2&gt;

&lt;p&gt;El 5 de marzo de 2026, AWS lanzó el &lt;a href="https://aws.amazon.com/about-aws/whats-new/2026/03/lambda-durable-kiro-power/?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Kiro Power para Lambda Durable Functions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Incluye guía especializada para todo el ciclo de desarrollo: desde el setup del proyecto y las reglas del replay model, pasando por steps, waits, callbacks, ejecución en paralelo, manejo de errores, testing local, hasta deploy con CDK, SAM, o CloudFormation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6g0u7skigd98cii83jhf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6g0u7skigd98cii83jhf.png" alt="Diagrama de lo que incluye el Power — replay model, steps, waits, error handling, testing, deployment" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No tienes que leer toda la documentación antes de empezar. El Power te da lo que necesitas, cuando lo necesitas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instalando el Power
&lt;/h2&gt;

&lt;p&gt;Esto es la parte más fácil. Literalmente un clic.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Abre Kiro&lt;/li&gt;
&lt;li&gt;Ve al panel de Powers (en la barra lateral)&lt;/li&gt;
&lt;li&gt;En la sección &lt;strong&gt;Available&lt;/strong&gt;, busca "Lambda Durable Functions"&lt;/li&gt;
&lt;li&gt;Haz clic en &lt;strong&gt;Install&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Eso es todo.&lt;/p&gt;

&lt;p&gt;No hay configuración adicional. No hay dependencias que instalar manualmente para el Power. Una vez instalado, el agente de Kiro ya tiene acceso a toda la guía especializada.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; No todos los servicios de AWS tienen un Power dedicado. Los Powers son módulos especializados que se van agregando con el tiempo. Puedes explorar los disponibles en el panel de Powers de Kiro.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ahora sí, vamos a construir algo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisitos
&lt;/h2&gt;

&lt;p&gt;Antes de escribir código, necesitas tener algunas herramientas instaladas. Los siguientes comandos te ayudan a verificar si ya las tienes. Si alguno falla, sigue el link de documentación correspondiente para instalarlo.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AWS CLI&lt;/strong&gt; instalado (versión 2.33.22 o superior) y &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;configurado con tus credenciales&lt;/a&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws &lt;span class="nt"&gt;--version&lt;/span&gt;
aws sts get-caller-identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js 22+&lt;/a&gt;&lt;/strong&gt; instalado (para este tutorial usaremos TypeScript):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AWS CDK&lt;/strong&gt; instalado globalmente (versión 2.237.1 o superior). Este se instala a nivel global en tu máquina, no dentro del proyecto:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; aws-cdk
cdk &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si prefieres Python, &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Lambda Durable Functions&lt;/a&gt; también soporta Python 3.11+. Pero para este tutorial vamos con TypeScript porque es el lenguaje que más uso y el que me resulta más natural para explicar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creando tu primer proyecto
&lt;/h2&gt;

&lt;p&gt;Abre Kiro y crea una carpeta nueva para tu proyecto. Luego, en el chat del agente, puedes pedirle directamente:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Crea un proyecto de Lambda Durable Function con TypeScript y CDK"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;El agente, con el Power instalado, va a:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inicializar el proyecto con la estructura correcta&lt;/li&gt;
&lt;li&gt;Instalar las dependencias necesarias&lt;/li&gt;
&lt;li&gt;Configurar TypeScript&lt;/li&gt;
&lt;li&gt;Crear los archivos base&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pero para que entiendas qué está pasando, vamos a hacerlo paso a paso.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inicializar el proyecto
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;image-processor
&lt;span class="nb"&gt;cd &lt;/span&gt;image-processor
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Instalar dependencias
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @aws/durable-execution-sdk-js
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @aws/durable-execution-sdk-js-testing typescript @types/node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ese primer paquete es el &lt;a href="https://github.com/aws/aws-durable-execution-sdk-js?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;SDK de Durable Execution&lt;/a&gt;. Es el core de las Durable Functions: lo que hace posible que tu función guarde checkpoints y sobreviva a fallos.&lt;/p&gt;

&lt;p&gt;El segundo paquete (&lt;code&gt;-testing&lt;/code&gt;) es para testear localmente sin necesidad de deployar a AWS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Estructura del proyecto
&lt;/h3&gt;

&lt;p&gt;Kiro va a generar esta estructura por ti cuando le pidas crear el proyecto. Pero si prefieres hacerlo manual, así es como debería verse:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image-processor/
├── src/
│   └── handler.ts          # Tu función durable
├── tests/
│   └── handler.test.ts     # Tests locales
├── infrastructure/
│   └── stack.ts             # CDK stack para deploy
├── tsconfig.json
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple. Sin carpetas innecesarias. Sin archivos que no vas a usar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tu primera Durable Function (generada por el Power)
&lt;/h2&gt;

&lt;p&gt;Ahora viene la parte divertida.&lt;/p&gt;

&lt;p&gt;¿Recuerdas el ejemplo del &lt;a href="https://dev.to/aws/lambda-durable-functions-mata-a-step-functions-o-no-2p6c"&gt;artículo anterior&lt;/a&gt;? Un usuario sube una foto y necesitas: validar que sea una imagen válida, redimensionar a diferentes tamaños, aplicar marca de agua, generar thumbnails, y guardar en S3.&lt;/p&gt;

&lt;p&gt;Abre el chat de Kiro y escríbele algo como:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Crea una Durable Function que procese imágenes: validar el archivo, redimensionar, aplicar marca de agua, generar thumbnails y guardar en S3"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;El agente, con el Power instalado, &lt;strong&gt;genera el código por ti&lt;/strong&gt;. No copia y pega de internet. Entiende las reglas del replay model, sabe qué va dentro de un step y qué no, y estructura el workflow siguiendo las buenas prácticas del SDK.&lt;/p&gt;

&lt;p&gt;Esto es lo que el Power generó para mí cuando le pedí exactamente eso:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Floq30by0lgdpwou1b7nv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Floq30by0lgdpwou1b7nv.png" alt="Screenshot del agente de Kiro generando el código de la Durable Function en el chat" width="800" height="464"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withDurableExecution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ImageEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DurableContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputBucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputBucket&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputPrefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputPrefix&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;processed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.[^&lt;/span&gt;&lt;span class="sr"&gt;.&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.[^&lt;/span&gt;&lt;span class="sr"&gt;.&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;)?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 1: Validate the file&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;validate-file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`File not found: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transformToByteArray&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tiff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Unsupported image format: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Supported: jpeg, png, webp, tiff`&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;File exceeds 50MB limit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;File validated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 2: Resize the image&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resizedBase64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize-image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxWidth&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxHeight&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inside&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;withoutEnlargement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resizedKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;outputPrefix&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-resized&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;outputBucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resizedKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;ContentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`image/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}));&lt;/span&gt;

      &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Image resized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resizedKey&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;resized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;retryStrategy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ver codigo completo: &lt;a href="https://github.com/hsaenzG/durable-funtions-demo/blob/main/src/handler.ts" rel="noopener noreferrer"&gt;https://github.com/hsaenzG/durable-funtions-demo/blob/main/src/handler.ts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Parece código normal, ¿verdad? Y lo es. Pero con superpoderes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Qué está pasando aquí
&lt;/h3&gt;

&lt;p&gt;Cada &lt;code&gt;context.step()&lt;/code&gt; es un &lt;strong&gt;checkpoint&lt;/strong&gt;. Si tu función falla aplicando la marca de agua (paso 3), cuando se reintente, &lt;strong&gt;no va a volver a validar ni redimensionar&lt;/strong&gt;. Salta directamente al paso 3 con los resultados que ya tenía guardados.&lt;/p&gt;

&lt;p&gt;Eso es el replay model en acción. Y para un workflow de procesamiento de imágenes, donde cada paso puede tardar segundos o minutos, no repetir trabajo es la diferencia entre una buena experiencia y una pesadilla.&lt;/p&gt;

&lt;p&gt;Fíjate también en &lt;code&gt;context.logger&lt;/code&gt; en lugar de &lt;code&gt;console.log&lt;/code&gt;: es &lt;strong&gt;replay-aware&lt;/strong&gt;. No duplica logs cuando la función se reintenta.&lt;/p&gt;

&lt;h2&gt;
  
  
  El Kiro Power y la regla de oro del Replay Model
&lt;/h2&gt;

&lt;p&gt;Aquí es donde el Kiro Power realmente brilla. Porque hay una regla que la mayoría no conocemos hasta que algo se rompe:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Todo código no determinístico DEBE ir dentro de un step.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Y el Power te avisa cuando estás a punto de violar esa regla. No tienes que memorizar todos los casos. El agente los detecta y te sugiere la corrección antes de que sea un problema.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1tzve877ywwkjmlhxyya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1tzve877ywwkjmlhxyya.png" alt="creenshot del agente de Kiro detectando una violación del replay model y sugiriendo la corrección" width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pero, ¿por qué importa tanto esta regla?&lt;/p&gt;

&lt;p&gt;Cuando una Durable Function se reintenta (replay), ejecuta todo el código desde el principio. Los steps que ya completaron se saltan (usa el resultado guardado). Pero el código que está &lt;strong&gt;fuera&lt;/strong&gt; de los steps se ejecuta de nuevo.&lt;/p&gt;

&lt;p&gt;Si ese código da un resultado diferente en el replay... las cosas se rompen.&lt;/p&gt;

&lt;p&gt;¿Qué es código no determinístico? Cualquier cosa que puede dar un resultado diferente cada vez:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Date.now()&lt;/code&gt; → diferente cada vez&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Math.random()&lt;/code&gt; → diferente cada vez&lt;/li&gt;
&lt;li&gt;Llamadas a APIs externas → pueden devolver datos diferentes&lt;/li&gt;
&lt;li&gt;Consultas a bases de datos → los datos pueden haber cambiado&lt;/li&gt;
&lt;li&gt;Generación de UUIDs → diferente cada vez&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Veamos un ejemplo concreto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ MAL: Date.now() fuera de un step&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Diferente en cada replay&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;save&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ BIEN: Date.now() dentro del step&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;save-with-timestamp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Se guarda con el checkpoint&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sin el Power, descubrir este tipo de errores es prueba y error. Con el Power, el agente te lo señala mientras escribes.&lt;/p&gt;

&lt;p&gt;Además, puedes instalar el plugin de ESLint para Durable Functions que atrapa estos errores en tiempo de desarrollo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @aws/durable-execution-sdk-js-eslint-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testeando localmente
&lt;/h2&gt;

&lt;p&gt;No necesitas hacer deploy para probar tu función. El SDK incluye un test runner local.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LocalDurableTestRunner&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws/durable-execution-sdk-js-testing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../src/handler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Image Processing Workflow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should process an image successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LocalDurableTestRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;skipTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-images-bucket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uploads/photo.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Verificar que el procesamiento se completó&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalKey&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uploads/photo.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Verificar que los steps se ejecutaron&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOperations&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validateStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;validate-image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resizeStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize-image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;watermarkStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apply-watermark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thumbnailStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;generate-thumbnails&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saveStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;op&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;save-to-s3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validateStep&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeDefined&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resizeStep&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeDefined&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;watermarkStep&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeDefined&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thumbnailStep&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeDefined&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;saveStep&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeDefined&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El &lt;code&gt;skipTime: true&lt;/code&gt; es clave: hace que los &lt;code&gt;wait&lt;/code&gt; se salten instantáneamente en los tests.&lt;/p&gt;

&lt;p&gt;Y fíjate que buscamos los steps &lt;strong&gt;por nombre&lt;/strong&gt;, no por índice. Eso es una buena práctica que el Power te recuerda constantemente: siempre nombra tus steps de forma descriptiva.&lt;/p&gt;

&lt;h2&gt;
  
  
  Haciendo deploy con CDK
&lt;/h2&gt;

&lt;p&gt;Ya tienes tu función funcionando localmente. Ahora toca subirla a AWS.&lt;/p&gt;

&lt;p&gt;Para esto usamos &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/home.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;AWS CDK&lt;/a&gt;, que te permite definir tu infraestructura en código (TypeScript, en nuestro caso).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-lambda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;logs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-logs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-iam&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DurableFunctionStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Log group explícito para mejor control&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;LogGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ImageProcessingLogs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;logGroupName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/aws/lambda/imageProcessing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;retention&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RetentionDays&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ONE_WEEK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;removalPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RemovalPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DESTROY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// La función durable&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ImageProcessing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODEJS_24_X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handler.handler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;logGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;logGroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;durableConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;executionTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;retentionPeriod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Política de permisos para durable execution&lt;/span&gt;
    &lt;span class="nx"&gt;imageFunction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;addManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAwsManagedPolicyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;service-role/AWSLambdaBasicDurableExecutionRolePolicy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Crear alias para invocación&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;imageFunction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentVersion&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alias&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ProdAlias&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;aliasName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CfnOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FunctionAliasArn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;functionArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Lo que importa aquí
&lt;/h3&gt;

&lt;p&gt;Hay tres cosas que no puedes olvidar al hacer deploy de una Durable Function:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;durableConfig&lt;/code&gt;&lt;/strong&gt;: Sin esto, tu función es una Lambda normal. El &lt;code&gt;executionTimeout&lt;/code&gt; define cuánto tiempo máximo puede correr tu workflow (hasta 1 año). El &lt;code&gt;retentionPeriod&lt;/code&gt; define cuánto tiempo se guardan los datos de ejecución.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;AWSLambdaBasicDurableExecutionRolePolicy&lt;/code&gt;&lt;/strong&gt;: Esta política le da a tu función permisos para hacer checkpoints (&lt;code&gt;lambda:CheckpointDurableExecution&lt;/code&gt;) y leer el estado de ejecución (&lt;code&gt;lambda:GetDurableExecutionState&lt;/code&gt;). Sin ella, los steps no pueden guardar su progreso.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ARN calificado&lt;/strong&gt;: Las Durable Functions &lt;strong&gt;requieren&lt;/strong&gt; un &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;ARN&lt;/a&gt; calificado para ser invocadas. Un ARN (Amazon Resource Name) es el identificador único de un recurso en AWS. "Calificado" significa que incluye la versión o alias de la función. No puedes invocar una Durable Function con el ARN sin calificar.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ✅ Correcto: con alias&lt;/span&gt;
aws lambda invoke &lt;span class="nt"&gt;--function-name&lt;/span&gt; &lt;span class="s1"&gt;'MiStack-MiFuncion-abc123:prod'&lt;/span&gt; response.json

&lt;span class="c"&gt;# ✅ Correcto: con versión&lt;/span&gt;
aws lambda invoke &lt;span class="nt"&gt;--function-name&lt;/span&gt; &lt;span class="s1"&gt;'MiStack-MiFuncion-abc123:1'&lt;/span&gt; response.json

&lt;span class="c"&gt;# ❌ Incorrecto: sin calificar — va a fallar&lt;/span&gt;
aws lambda invoke &lt;span class="nt"&gt;--function-name&lt;/span&gt; MiStack-MiFuncion-abc123 response.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; CDK genera nombres de recursos con un sufijo aleatorio (ej: &lt;code&gt;ImageProcessorStack-ImageProcessor5D0B0257-lBARjqDaXdWK&lt;/code&gt;). Puedes encontrar el nombre exacto de tu función en el output del &lt;code&gt;cdk deploy&lt;/code&gt; o en la consola de AWS Lambda.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Deploy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Compilar TypeScript&lt;/span&gt;
npx tsc

&lt;span class="c"&gt;# Deploy con CDK&lt;/span&gt;
cdk deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CDK se encarga de crear todo: la función, el rol de IAM, los permisos, el log group, la versión y el alias.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnds21mbsr10rh5lkkvow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnds21mbsr10rh5lkkvow.png" alt="IScreenshot del deploy con CDK ejecutándose en la terminal" width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Invocando tu función
&lt;/h2&gt;

&lt;p&gt;Ya está en la nube. Ahora vamos a probarla.&lt;/p&gt;

&lt;p&gt;Primero, sube una imagen de prueba al bucket de S3 que CDK creó:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;tu-imagen.jpg &lt;span class="se"&gt;\&lt;/span&gt;
  s3://TU-BUCKET-S3/photos/test.jpg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--profile&lt;/span&gt; TU-PERFIL &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; El nombre del bucket lo genera CDK automáticamente (ej: &lt;code&gt;imageprocessorstack-imagebucket97210811-x5j3e61lysxh&lt;/code&gt;). Puedes encontrarlo en el output del &lt;code&gt;cdk deploy&lt;/code&gt; o en la consola de S3.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ahora sí, invoca la función:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws lambda invoke &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--function-name&lt;/span&gt; &lt;span class="s1"&gt;'TU-NOMBRE-DE-FUNCION:prod'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--invocation-type&lt;/span&gt; Event &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--durable-execution-name&lt;/span&gt; &lt;span class="s2"&gt;"test-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--payload&lt;/span&gt; &lt;span class="s1"&gt;'{"bucket":"TU-BUCKET-S3","key":"photos/test.jpg"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cli-binary-format&lt;/span&gt; raw-in-base64-out &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--profile&lt;/span&gt; TU-PERFIL &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="se"&gt;\&lt;/span&gt;
  response.json

&lt;span class="nb"&gt;cat &lt;/span&gt;response.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Reemplaza &lt;code&gt;TU-NOMBRE-DE-FUNCION&lt;/code&gt;, &lt;code&gt;TU-BUCKET-S3&lt;/code&gt; y &lt;code&gt;TU-PERFIL&lt;/code&gt; con los valores reales de tu entorno. Revisa los outputs del &lt;code&gt;cdk deploy&lt;/code&gt; para encontrarlos.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0nkoicfiymikbsnby8we.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0nkoicfiymikbsnby8we.png" alt="Screenshot de la respuesta JSON en la terminal después de invocar la función" width="800" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Algunos detalles sobre estos parámetros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--invocation-type Event&lt;/code&gt;&lt;/strong&gt;: Invoca la función de forma asíncrona. Para workflows largos como procesamiento de imágenes, esto es lo recomendado. Si quieres esperar la respuesta, usa &lt;code&gt;RequestResponse&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--durable-execution-name "test-$(date +%s)"&lt;/code&gt;&lt;/strong&gt;: Genera un nombre único por ejecución usando un timestamp. Esto garantiza &lt;strong&gt;idempotencia&lt;/strong&gt;: si invocas con el mismo nombre dos veces, la segunda no crea una ejecución nueva.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--profile&lt;/code&gt; y &lt;code&gt;--region&lt;/code&gt;&lt;/strong&gt;: Si tienes múltiples perfiles de AWS configurados, no olvides especificarlos.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Monitoreando tus ejecuciones
&lt;/h2&gt;

&lt;p&gt;Ya invocaste tu función. ¿Y ahora? ¿Cómo sabes si funcionó?&lt;/p&gt;

&lt;p&gt;No estás a ciegas. La consola de AWS Lambda tiene una interfaz dedicada para Durable Functions donde puedes ver todo lo que está pasando.&lt;/p&gt;

&lt;p&gt;Ve a tu función en la consola y busca la pestaña &lt;strong&gt;Durable executions&lt;/strong&gt;. Ahí vas a encontrar la lista de todas las ejecuciones con su estado: running, completed, failed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1w5zhcbyzwd0l8gko8n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1w5zhcbyzwd0l8gko8n.png" alt="Screenshot de la consola de AWS Lambda mostrando la lista de ejecuciones durables con sus estados" width="800" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Haz clic en cualquier ejecución y vas a ver el detalle completo: qué steps se completaron, cuáles fallaron, cuánto tardó cada uno, y los datos que se guardaron en cada checkpoint.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9c9g5im3wvekd34m1ol2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9c9g5im3wvekd34m1ol2.png" alt="Screenshot del detalle de una ejecución durable mostrando los steps completados y sus resultados" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esto es oro para debuggear. Si algo falló en el paso 3, puedes ver exactamente qué datos tenía en ese momento y qué error se produjo. Sin adivinar. Sin buscar en CloudWatch a ciegas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué esto importa si estás empezando
&lt;/h2&gt;

&lt;p&gt;Sin el Power, el camino típico es: leer toda la documentación, tropezar con el replay model, configurar el proyecto a mano, y descubrir errores de deploy por prueba y error.&lt;/p&gt;

&lt;p&gt;Con el Power, el agente te guía en cada paso. No elimina la necesidad de entender los conceptos (eso sigue siendo importante). Pero reduce drásticamente el tiempo entre "quiero aprender Durable Functions" y "tengo una funcionando en producción".&lt;/p&gt;

&lt;h2&gt;
  
  
  Limpieza de recursos
&lt;/h2&gt;

&lt;p&gt;Si solo estás probando, recuerda eliminar los recursos para evitar cargos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cdk destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto elimina la función Lambda, el rol de IAM, el log group, y todos los recursos asociados. CDK se encarga de todo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que aprendiste
&lt;/h2&gt;

&lt;p&gt;Instalaste un Kiro Power con un clic, creaste un proyecto desde cero, dejaste que el agente generara tu primera Durable Function con el mismo ejemplo de procesamiento de imágenes del artículo anterior, entendiste la regla de oro del replay model, testeaste localmente sin deploy, y subiste todo a AWS con CDK.&lt;/p&gt;

&lt;p&gt;De cero a producción. Con IA guiándote en cada paso.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;Ya tienes el workflow de procesamiento de imágenes funcionando. Pero lo construimos en el camino feliz: todo sale bien, nada falla.&lt;/p&gt;

&lt;p&gt;En el próximo artículo vamos a &lt;strong&gt;romper cosas a propósito&lt;/strong&gt;. Vamos a ver cómo los checkpoints automáticos mantienen tu progreso incluso cuando algo truena, y cómo el replay model recupera la ejecución exactamente donde quedó.&lt;/p&gt;

&lt;p&gt;Código real. Fallos reales. Recuperación real.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Te resultó útil este artículo?&lt;/strong&gt; Compártelo con tu equipo o déjame saber en los comentarios qué otros temas de serverless te gustaría que cubriera. Y si ya instalaste el Kiro Power y estás construyendo tu primera Durable Function, me encantaría escuchar tu experiencia.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Cold Starts en AWS Lambda: Qué son, por qué ocurren y cómo solucionarlos</title>
      <dc:creator>Hazel Saenz</dc:creator>
      <pubDate>Mon, 16 Mar 2026 17:08:25 +0000</pubDate>
      <link>https://dev.to/aws/cold-starts-en-aws-lambda-que-son-por-que-ocurren-y-como-solucionarlos-13no</link>
      <guid>https://dev.to/aws/cold-starts-en-aws-lambda-que-son-por-que-ocurren-y-como-solucionarlos-13no</guid>
      <description>&lt;p&gt;&lt;em&gt;Todo lo que necesitas saber sobre cold starts en Lambda y las estrategias para reducir su impacto.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;La primera vez que me topé con un cold start, no tenía idea de qué estaba pasando.&lt;/p&gt;

&lt;p&gt;Había terminado de deployar una API con Lambda para un proyecto en el que estaba trabajando. Todo funcionaba. Los tests pasaban. El endpoint respondía rápido. Estaba feliz.&lt;/p&gt;

&lt;p&gt;Hasta que un compañero me escribió: "Oye, a veces la API tarda como 3 segundos en responder. ¿Es normal?"&lt;/p&gt;

&lt;p&gt;Mi primera reacción fue: "No puede ser, a mí me responde en 200ms". Así que abrí CloudWatch, empecé a revisar los logs... y ahí lo vi. Un &lt;code&gt;INIT&lt;/code&gt; duration de 2.8 segundos que aparecía de vez en cuando, sin patrón aparente. No había error. No había timeout. Solo... lentitud.&lt;/p&gt;

&lt;p&gt;Pasé un buen rato buscando qué significaba ese &lt;code&gt;INIT&lt;/code&gt;. ¿Era un bug? ¿Algo que configuré mal? ¿Un problema de red?&lt;/p&gt;

&lt;p&gt;No. Era un cold start.&lt;/p&gt;

&lt;p&gt;Y cuando entendí qué era y por qué pasaba, todo empezó a tener sentido. Esas peticiones lentas no eran aleatorias. Eran el precio de no tener servidores corriendo 24/7.&lt;/p&gt;

&lt;p&gt;Si alguna vez sentiste que tu función Lambda "se dormía" y tardaba en despertar, este artículo es para ti. Te voy a contar todo lo que aprendí sobre cold starts: qué son, por qué ocurren, y las estrategias que uso para reducir su impacto.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Qué es un cold start?
&lt;/h2&gt;

&lt;p&gt;Cuando invocas una función Lambda, AWS necesita un lugar donde ejecutar tu código. Ese lugar se llama &lt;strong&gt;execution environment&lt;/strong&gt;: un contenedor aislado con su runtime, tu código, y las dependencias que necesita.&lt;/p&gt;

&lt;p&gt;Si ya existe un environment disponible (porque tu función se ejecutó hace poco), Lambda lo reutiliza. Eso es un &lt;strong&gt;warm start&lt;/strong&gt;. Rápido. Sin sorpresas.&lt;/p&gt;

&lt;p&gt;Pero si no hay ninguno disponible — porque es la primera invocación, porque pasó mucho tiempo sin actividad, o porque hay un pico de tráfico y Lambda necesita crear más environments — entonces tiene que crear uno nuevo desde cero.&lt;/p&gt;

&lt;p&gt;Eso es un &lt;strong&gt;cold start&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyofixknlxsvwhity8ieq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyofixknlxsvwhity8ieq.png" alt="Diagrama comparando warm start vs cold start — mostrando las fases adicionales del cold start" width="800" height="142"&gt;&lt;/a&gt;&lt;br&gt;
Y crear un environment desde cero no es instantáneo. Lambda tiene que hacer varias cosas antes de que tu código empiece a ejecutarse:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Container Provisioning&lt;/strong&gt;: asignar los recursos de cómputo según la memoria configurada&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime Initialization&lt;/strong&gt;: cargar el runtime del lenguaje (Python, Node.js, Java, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Function Code Loading&lt;/strong&gt;: descargar y desempaquetar tu código&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency Resolution&lt;/strong&gt;: cargar las librerías y paquetes que tu función necesita&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Todo eso es la &lt;strong&gt;fase de inicialización&lt;/strong&gt; (Init phase). Y esa latencia adicional es lo que llamamos cold start.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkvsz28wfsv0kansff8sp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkvsz28wfsv0kansff8sp.png" alt="Diagrama de las fases del cold start — Container Provisioning → Runtime Init → Code Loading → Dependency Resolution → Function Execution" width="800" height="105"&gt;&lt;/a&gt;&lt;br&gt;
La buena noticia: el cold start solo ocurre &lt;strong&gt;una vez&lt;/strong&gt; por ciclo de vida del environment. Una vez que el environment está listo, las siguientes invocaciones son warm starts.&lt;/p&gt;

&lt;p&gt;La otra buena noticia: según AWS, los cold starts típicamente afectan &lt;strong&gt;menos del 1% de las invocaciones&lt;/strong&gt;. Pero ese 1% puede ser un problema si tu aplicación es sensible a la latencia.&lt;/p&gt;
&lt;h2&gt;
  
  
  ¿Por qué ocurren?
&lt;/h2&gt;

&lt;p&gt;Los cold starts no son un bug. Son una consecuencia natural de cómo funciona el modelo serverless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Eficiencia de recursos&lt;/strong&gt;: no pagas cuando tu código no se ejecuta. Eso es genial para tu billetera, pero significa que Lambda apaga los environments que llevan tiempo sin usarse. Cuando llega una nueva invocación, tiene que crear uno nuevo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aislamiento de seguridad&lt;/strong&gt;: cada execution environment es independiente. No comparte memoria ni estado con otros. Esa seguridad requiere crear un environment fresco cada vez.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auto-scaling&lt;/strong&gt;: cuando llega un pico de tráfico, Lambda crea nuevos environments para manejar la carga. Cada environment nuevo pasa por la fase de inicialización.&lt;/p&gt;

&lt;p&gt;En resumen: los cold starts son el precio que pagas por no tener que administrar servidores. Y en la mayoría de los casos, es un precio muy bajo.&lt;/p&gt;

&lt;p&gt;Pero cuando sí importa — APIs de usuario, microservicios interactivos, flujos donde cada segundo cuenta — necesitas saber cómo reducirlos.&lt;/p&gt;
&lt;h2&gt;
  
  
  Factores que influyen en la duración
&lt;/h2&gt;

&lt;p&gt;No todos los cold starts son iguales. Algunos duran 100 milisegundos. Otros duran 5 segundos. La diferencia depende de varios factores.&lt;/p&gt;
&lt;h3&gt;
  
  
  Runtime
&lt;/h3&gt;

&lt;p&gt;Los lenguajes interpretados como Python y Node.js inicializan más rápido que los compilados como Java o .NET.&lt;/p&gt;

&lt;p&gt;¿Por qué? Java necesita cargar la JVM y hacer JIT compilation. .NET tiene un proceso similar. Python y Node.js simplemente cargan el intérprete y empiezan a ejecutar.&lt;/p&gt;

&lt;p&gt;Eso no significa que debas elegir tu lenguaje solo por el cold start. Pero es un factor a considerar si la latencia es crítica.&lt;/p&gt;
&lt;h3&gt;
  
  
  Tamaño del paquete
&lt;/h3&gt;

&lt;p&gt;Más grande el paquete = más tiempo para descargarlo, descomprimirlo e inicializarlo.&lt;/p&gt;

&lt;p&gt;Si tu función incluye dependencias que no usa, estás pagando cold start por código que nunca se ejecuta. Un paquete de 5 MB inicializa mucho más rápido que uno de 250 MB.&lt;/p&gt;
&lt;h3&gt;
  
  
  Memoria asignada
&lt;/h3&gt;

&lt;p&gt;Esto es contraintuitivo: &lt;strong&gt;más memoria = cold starts más rápidos&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;¿Por qué? Porque Lambda asigna CPU proporcionalmente a la memoria. Más memoria significa más CPU, y más CPU significa que la inicialización se completa más rápido.&lt;/p&gt;

&lt;p&gt;A veces, subir la memoria de 128 MB a 512 MB reduce el cold start significativamente, y el costo adicional es mínimo porque la función termina más rápido.&lt;/p&gt;
&lt;h3&gt;
  
  
  Configuración de red (VPC)
&lt;/h3&gt;

&lt;p&gt;Si tu función está dentro de una &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-vpc.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;VPC&lt;/a&gt;, Lambda necesita crear una Elastic Network Interface (ENI) para conectarla a la red. Eso agrega latencia al cold start.&lt;/p&gt;

&lt;p&gt;AWS mejoró esto significativamente con &lt;a href="https://aws.amazon.com/blogs/compute/announcing-improved-vpc-networking-for-aws-lambda-functions/?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Hyperplane&lt;/a&gt;, que redujo la latencia de VPC de segundos a milisegundos. Pero sigue siendo un factor si tu función necesita acceso a recursos dentro de una VPC.&lt;/p&gt;
&lt;h3&gt;
  
  
  Inicialización estática
&lt;/h3&gt;

&lt;p&gt;Todo el código que se ejecuta &lt;strong&gt;fuera del handler&lt;/strong&gt; se ejecuta durante la fase Init. Si estás importando 15 librerías, creando conexiones a bases de datos, y cargando archivos de configuración... todo eso suma al cold start.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Todo esto se ejecuta durante el Init (cold start)
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LLMChain&lt;/span&gt;

&lt;span class="n"&gt;s3_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dynamodb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my-table&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Esto se ejecuta en cada invocación (warm o cold)
&lt;/span&gt;    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La inicialización estática es un arma de doble filo: quieres inicializar cosas fuera del handler para reutilizarlas en warm starts, pero todo lo que pongas ahí aumenta el cold start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estrategias de optimización (sin costo adicional)
&lt;/h2&gt;

&lt;p&gt;Antes de llegar a las soluciones de AWS que cuestan dinero, hay varias cosas que puedes hacer gratis.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Importa solo lo necesario
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ Importa todo el SDK
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="c1"&gt;# ✅ Importa solo lo que necesitas
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;
&lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Mantén tus paquetes pequeños
&lt;/h3&gt;

&lt;p&gt;Elimina dependencias que no uses. Usa &lt;a href="https://aws.amazon.com/blogs/compute/optimizing-node-js-dependencies-in-aws-lambda/?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;tree shaking&lt;/a&gt; en Node.js. Excluye archivos de tests, documentación, y ejemplos de tus paquetes de deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Inicializa clientes fuera del handler
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ✅ Se inicializa una vez y se reutiliza en warm starts
&lt;/span&gt;&lt;span class="n"&gt;s3_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my-bucket&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my-key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Asigna memoria adecuada
&lt;/h3&gt;

&lt;p&gt;No te quedes con los 128 MB por defecto si tu función carga dependencias pesadas. Experimenta con 256 MB, 512 MB, o más. Usa &lt;a href="https://docs.aws.amazon.com/lambda/latest/operatorguide/profile-functions.html/?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;AWS Lambda Power Tuning&lt;/a&gt; para encontrar el balance óptimo entre costo y rendimiento.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Usa layers para dependencias compartidas
&lt;/h3&gt;

&lt;p&gt;Si varias funciones usan las mismas dependencias, empaquétalas en un &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html/?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Lambda Layer&lt;/a&gt;. Esto no reduce el cold start directamente, pero te ayuda a mantener paquetes organizados y más fáciles de optimizar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provisioned Concurrency: environments siempre calientes
&lt;/h2&gt;

&lt;p&gt;Si las optimizaciones de código no son suficientes, AWS tiene dos soluciones dedicadas. La primera es &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/provisioned-concurrency.html/?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Provisioned Concurrency&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;La idea es simple: le dices a Lambda "mantén X environments siempre listos". Lambda los pre-inicializa y los mantiene calientes, esperando invocaciones.&lt;/p&gt;

&lt;p&gt;Resultado: &lt;strong&gt;cero cold starts&lt;/strong&gt; para esas invocaciones. Respuestas en milisegundos de doble dígito.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Configurar 10 environments siempre listos&lt;/span&gt;
aws lambda put-provisioned-concurrency-config &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--function-name&lt;/span&gt; my-function &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--qualifier&lt;/span&gt; prod &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--provisioned-concurrent-executions&lt;/span&gt; 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cuándo usarlo
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Tu aplicación tiene requisitos estrictos de latencia (APIs de usuario, microservicios interactivos)&lt;/li&gt;
&lt;li&gt;El tráfico es predecible o puedes configurar auto-scaling&lt;/li&gt;
&lt;li&gt;Estás dispuesto a pagar por environments que están listos aunque no se usen&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cuándo NO usarlo
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Tráfico esporádico o impredecible (vas a pagar por environments idle)&lt;/li&gt;
&lt;li&gt;Workloads asíncronos donde la latencia no es crítica (procesamiento de datos, colas)&lt;/li&gt;
&lt;li&gt;Tu presupuesto es ajustado&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Nota importante:&lt;/strong&gt; Provisioned Concurrency tiene un costo adicional. Pagas por cada environment provisionado, esté o no procesando invocaciones. Pero si la latencia es crítica para tu negocio, el costo vale la pena.&lt;/p&gt;

&lt;p&gt;También puedes combinar Provisioned Concurrency con &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/provisioned-concurrency.html#managing-provisioned-concurency/?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Application Auto Scaling&lt;/a&gt; para ajustar automáticamente la cantidad de environments según el tráfico. Puedes usar scheduled scaling (para patrones predecibles) o target tracking (para ajuste dinámico).&lt;/p&gt;

&lt;h2&gt;
  
  
  SnapStart: snapshots del environment inicializado
&lt;/h2&gt;

&lt;p&gt;La segunda solución es &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Lambda SnapStart&lt;/a&gt;. Y funciona de una manera completamente diferente.&lt;/p&gt;

&lt;p&gt;En lugar de mantener environments calientes, SnapStart toma una &lt;strong&gt;foto&lt;/strong&gt; (snapshot) del environment después de que se inicializa. Cuando llega una invocación, en lugar de inicializar desde cero, Lambda &lt;strong&gt;restaura&lt;/strong&gt; el environment desde esa foto.&lt;/p&gt;

&lt;p&gt;El resultado: cold starts que bajan de varios segundos a &lt;strong&gt;sub-segundo&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj1jn48dxx7m1l6z8b3xy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj1jn48dxx7m1l6z8b3xy.png" alt="Diagrama comparando el flujo normal (Init completo) vs el flujo con SnapStart" width="800" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Runtimes soportados
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Java 11+&lt;/li&gt;
&lt;li&gt;Python 3.12+&lt;/li&gt;
&lt;li&gt;.NET 8+&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cuándo usarlo
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Funciones con inicialización pesada (dependencias grandes, frameworks, modelos ML)&lt;/li&gt;
&lt;li&gt;Invocaciones frecuentes a escala&lt;/li&gt;
&lt;li&gt;Quieres reducir cold starts sin el costo de Provisioned Concurrency&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cuándo NO usarlo
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Funciones que ya inicializan en milisegundos (no vas a notar la diferencia)&lt;/li&gt;
&lt;li&gt;Necesitas latencia garantizada de doble dígito de milisegundos (usa Provisioned Concurrency)&lt;/li&gt;
&lt;li&gt;Tu función usa EFS, Provisioned Concurrency, o almacenamiento efímero mayor a 512 MB (no es compatible)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SnapStart es especialmente interesante para &lt;strong&gt;Python&lt;/strong&gt;, donde cargar dependencias como Pandas, NumPy, LangChain, o frameworks como Flask y Django puede tomar varios segundos.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;En el próximo artículo de esta serie, vamos a profundizar en SnapStart para Python&lt;/strong&gt;: cómo funciona internamente, cómo activarlo paso a paso, cómo usar runtime hooks, y las mejores prácticas para sacarle el máximo provecho.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Cuál estrategia elegir?
&lt;/h2&gt;

&lt;p&gt;No es "una u otra". Puedes (y deberías) combinarlas.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Estrategia&lt;/th&gt;
&lt;th&gt;Latencia&lt;/th&gt;
&lt;th&gt;Costo adicional&lt;/th&gt;
&lt;th&gt;Ideal para&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Optimización de código&lt;/td&gt;
&lt;td&gt;Reduce el Init&lt;/td&gt;
&lt;td&gt;Ninguno&lt;/td&gt;
&lt;td&gt;Siempre. Es el primer paso&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SnapStart&lt;/td&gt;
&lt;td&gt;Sub-segundo&lt;/td&gt;
&lt;td&gt;Costo de cache + restauración&lt;/td&gt;
&lt;td&gt;Funciones con init pesado a escala&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provisioned Concurrency&lt;/td&gt;
&lt;td&gt;Milisegundos&lt;/td&gt;
&lt;td&gt;Pago por environment provisionado&lt;/td&gt;
&lt;td&gt;Latencia ultra-baja garantizada&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La recomendación:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Siempre&lt;/strong&gt; optimiza tu código primero. Es gratis y ayuda en todos los escenarios.&lt;/li&gt;
&lt;li&gt;Si después de optimizar sigues teniendo cold starts largos y tu función se invoca frecuentemente, &lt;strong&gt;prueba SnapStart&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Si necesitas latencia garantizada de milisegundos sin excepciones, &lt;strong&gt;usa Provisioned Concurrency&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;📊 Árbol de decisión (haz clic para expandir)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fguj5dptyxiu7epcx2xds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fguj5dptyxiu7epcx2xds.png" alt="Diagrama comparando el flujo normal (Init completo) vs el flujo con SnapStart (restaurar desde snapshot)" width="800" height="1779"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoreo: cómo saber si tienes un problema
&lt;/h2&gt;

&lt;p&gt;Antes de optimizar, necesitas medir. No asumas que tienes un problema de cold starts solo porque "a veces se siente lento".&lt;/p&gt;

&lt;p&gt;Si quieres ir más a fondo en el tema de diagnóstico y optimización de rendimiento en Lambda, te recomiendo este artículo donde comparto mi experiencia: &lt;a href="https://dev.to/aws-espanol/eleva-el-rendimiento-de-aws-lambda-7il"&gt;Eleva el rendimiento de AWS Lambda&lt;/a&gt;. Ahí cubro métricas de invocación, benchmarks, y estrategias prácticas para llevar tus funciones al siguiente nivel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que aprendiste
&lt;/h2&gt;

&lt;p&gt;Los cold starts son parte natural del modelo serverless. No son un bug, son un trade-off por no administrar servidores. Afectan menos del 1% de las invocaciones, pero cuando importan, importan mucho.&lt;/p&gt;

&lt;p&gt;La duración depende del runtime, el tamaño del paquete, la memoria asignada, y la configuración de red. Optimizar el código es siempre el primer paso y no cuesta nada. Provisioned Concurrency y SnapStart son las dos soluciones de AWS cuando necesitas ir más allá.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;Ya entiendes qué son los cold starts y las estrategias para combatirlos. En el próximo artículo vamos a profundizar en &lt;strong&gt;Lambda SnapStart para Python&lt;/strong&gt;: cómo funciona por debajo (Firecracker snapshots, cache en dos niveles), cómo activarlo paso a paso, cómo usar runtime hooks, y las mejores prácticas para sacarle el máximo provecho a tus funciones Python.&lt;/p&gt;

&lt;p&gt;Código real. Configuración real. Resultados reales.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Te resultó útil este artículo?&lt;/strong&gt; Compártelo con tu equipo o déjame saber en los comentarios qué otros temas de serverless te gustaría que cubriera. Y si ya estás lidiando con cold starts en producción, me encantaría escuchar cómo los resolviste.&lt;/p&gt;

</description>
      <category>lambda</category>
      <category>performance</category>
      <category>español</category>
      <category>aws</category>
    </item>
    <item>
      <title>Lambda Durable Functions mata a Step Functions... ¿o no?</title>
      <dc:creator>Hazel Saenz</dc:creator>
      <pubDate>Thu, 12 Mar 2026 00:26:59 +0000</pubDate>
      <link>https://dev.to/aws/lambda-durable-functions-mata-a-step-functions-o-no-2p6c</link>
      <guid>https://dev.to/aws/lambda-durable-functions-mata-a-step-functions-o-no-2p6c</guid>
      <description>&lt;p&gt;La verdad sobre cuándo usar cada una (y por qué necesitas entender ambas).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Al final de este artículo, sabrás exactamente cuándo elegir cada una.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Cuando AWS anunció &lt;a href="https://aws.amazon.com/blogs/aws/build-multi-step-applications-and-ai-workflows-with-aws-lambda-durable-functions/?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Lambda Durable Functions&lt;/a&gt; en re:Invent 2025, mi primera reacción fue:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Espera... ¿no es eso lo que hace Step Functions?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Y no fui la única. En conversaciones con otros devs, la pregunta era siempre la misma:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Por qué dos herramientas para lo mismo?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Si ya tengo &lt;a href="https://docs.aws.amazon.com/step-functions/?trk=b4df06f7-1a05-4faf-a488-43ff27da389d&amp;amp;sc_channel=el" rel="noopener noreferrer"&gt;Step Functions&lt;/a&gt; orquestando workflows, ¿para qué necesito Durable Functions? ¿Es un reemplazo? ¿Debería migrar todo? Pasé días leyendo la documentación, probando ejemplos, y tratando de entender la diferencia real. Y descubrí algo que casi nadie estaba diciendo:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No son lo mismo. Ni siquiera intentan serlo.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  La respuesta corta: No
&lt;/h2&gt;

&lt;p&gt;Lambda Durable Functions no mata a Step Functions. Son dos filosofías diferentes para resolver el mismo problema. Step Functions orquesta visualmente, Durable Functions orquesta en código. Y aquí está lo interesante: ambas tienen su lugar. No es "una u otra". Es "cuál para qué".&lt;/p&gt;

&lt;h2&gt;
  
  
  El problema que ambas resuelven
&lt;/h2&gt;

&lt;p&gt;Antes de hablar de diferencias, hay que entender qué problema están resolviendo.&lt;/p&gt;

&lt;p&gt;Imagina que estás construyendo un sistema de procesamiento de imágenes. Un usuario sube una foto y necesitas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Validar que el archivo es una imagen válida&lt;/li&gt;
&lt;li&gt;Redimensionar a diferentes tamaños&lt;/li&gt;
&lt;li&gt;Aplicar marca de agua&lt;/li&gt;
&lt;li&gt;Generar thumbnails&lt;/li&gt;
&lt;li&gt;Optimizar y guardar en S3&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Suena simple, ¿no?&lt;/p&gt;

&lt;p&gt;Pero cada paso puede fallar.&lt;br&gt;&lt;br&gt;
Cada paso puede tardar segundos... o minutos.&lt;br&gt;&lt;br&gt;
Y necesitas mantener el estado del procesamiento a través de todo el flujo.&lt;/p&gt;

&lt;p&gt;Si la validación falla, no puedes continuar.&lt;br&gt;&lt;br&gt;
Si el redimensionamiento truena, necesitas reintentarlo sin volver a validar.&lt;br&gt;&lt;br&gt;
Si algo falla a la mitad, necesitas saber exactamente dónde quedó todo.&lt;/p&gt;

&lt;p&gt;Esto es &lt;strong&gt;orquestación de workflows&lt;/strong&gt;: coordinar múltiples pasos sin perder el estado, manejando errores y reintentos de forma elegante.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnfzbyb1binxdui7s5bk4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnfzbyb1binxdui7s5bk4.png" alt="workflow" width="800" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tanto Step Functions como Durable Functions resuelven esto. Pero lo hacen de maneras completamente diferentes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step Functions: El enfoque visual
&lt;/h2&gt;

&lt;p&gt;Step Functions es &lt;strong&gt;declarativo&lt;/strong&gt;. Eso significa que defines &lt;em&gt;qué&lt;/em&gt; debe pasar, no &lt;em&gt;cómo&lt;/em&gt; hacerlo. Piensa en pedir un Uber: dices "llévame al aeropuerto" y alguien más decide la ruta, los giros, las autopistas. Tú solo declaras el destino.&lt;/p&gt;

&lt;p&gt;Así se ve un workflow en Step Functions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjsc39ypuhxs2zw0a6gu7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjsc39ypuhxs2zw0a6gu7.png" alt="Step Functions" width="692" height="1148"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En el código del workflow en Step Functions no estás escribiendo código que ejecuta cada paso. Estás describiendo el flujo. Y AWS se encarga del resto: mantener el estado, manejar errores, reintentar cuando falla algo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"StartAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ValidateImage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"States"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ValidateImage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:lambda:...:function:ValidateImage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ResizeImage"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ResizeImage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:lambda:...:function:ResizeImage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ApplyWatermark"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ApplyWatermark"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:lambda:...:function:ApplyWatermark"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GenerateThumbnails"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"GenerateThumbnails"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:lambda:...:function:GenerateThumbnails"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SaveToS3"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"SaveToS3"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:lambda:...:function:SaveToS3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"End"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cuándo brilla Step Functions
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyv7h8zr9yyrmn67l9lh1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyv7h8zr9yyrmn67l9lh1.png" alt="Step Functions" width="800" height="336"&gt;&lt;/a&gt;&lt;br&gt;
Hay escenarios donde este enfoque visual es perfecto:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Workflows con múltiples ramas y condiciones complejas&lt;/strong&gt;&lt;br&gt;
Si tu flujo tiene muchos "si esto, entonces aquello", Step Functions te deja verlo todo de un vistazo.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Equipos no técnicos necesitan entender la lógica&lt;/strong&gt;&lt;br&gt;
Product managers, QA, stakeholders... todos pueden abrir la consola de AWS y ver exactamente qué está pasando.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;El workflow cambia frecuentemente&lt;/strong&gt;&lt;br&gt;
Ajustar un JSON es más rápido que reescribir código, hacer tests y deployar.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integraciones directas con servicios AWS&lt;/strong&gt;&lt;br&gt;
DynamoDB, SNS, SQS... Step Functions los conecta sin escribir código.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Lambda Durable Functions: El enfoque code-first
&lt;/h2&gt;

&lt;p&gt;Durable Functions es completamente diferente. Aquí no describes el flujo. Lo escribes.&lt;/p&gt;

&lt;p&gt;Es &lt;strong&gt;code-first&lt;/strong&gt;: código que ejecuta paso a paso, con checkpoints automáticos que mantienen el estado. Siguiendo la analogía del Uber: aquí tú manejas. Gira a la izquierda, sigue derecho tres cuadras, toma la autopista, sal en la salida 12. Tú controlas cada paso del camino.&lt;/p&gt;

&lt;p&gt;Así se ve el mismo workflow en Durable Functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withDurableExecution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DurableContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws/durable-execution-sdk-js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withDurableExecution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DurableContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Cada step es un checkpoint automático&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;validate-image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
        &lt;span class="nf"&gt;validateImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resize-image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
        &lt;span class="nf"&gt;resizeImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;watermarked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apply-watermark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
        &lt;span class="nf"&gt;applyWatermark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;watermarked&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parece código normal, ¿verdad? Y lo es. Pero con un superpoder: cada &lt;code&gt;step&lt;/code&gt; es un checkpoint. Si algo falla, el workflow se reanuda exactamente donde quedó. No pierdes estado. No repites trabajo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cuándo brilla Durable Functions
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkucx9omblni06iyt9r9w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkucx9omblni06iyt9r9w.png" alt="Durable Functions" width="800" height="336"&gt;&lt;/a&gt;&lt;br&gt;
Este enfoque tiene sus propios momentos de gloria:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Workflows secuenciales con lógica compleja&lt;/strong&gt; &lt;br&gt;
Si tu flujo es más "haz esto, luego esto, luego esto" y menos "si esto entonces aquello", el código es más natural.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tu equipo vive en el IDE&lt;/strong&gt;&lt;br&gt;
Developers que prefieren escribir código, hacer tests unitarios, usar breakpoints... esto se siente como casa.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Necesitas reutilizar lógica existente&lt;/strong&gt;&lt;br&gt;
Si ya tienes funciones de negocio en tu codebase, las puedes llamar directamente.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Transformaciones de datos complejas entre pasos&lt;/strong&gt;&lt;br&gt;
Escribir esa lógica en código es más claro que en JSON.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Las diferencias que importan
&lt;/h2&gt;

&lt;p&gt;Ahora que viste ambos enfoques, aquí están las diferencias que realmente afectan tu decisión:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspecto&lt;/th&gt;
&lt;th&gt;Step Functions&lt;/th&gt;
&lt;th&gt;Durable Functions&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Modelo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Declarativo (JSON/YAML)&lt;/td&gt;
&lt;td&gt;Code-first (Python, Node.js, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Visualización&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Nativa en consola AWS&lt;/td&gt;
&lt;td&gt;Requiere herramientas externas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Debugging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Logs y trazas visuales&lt;/td&gt;
&lt;td&gt;Breakpoints tradicionales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complejidad&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Excelente para flujos con branches&lt;/td&gt;
&lt;td&gt;Excelente para lógica secuencial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Curva de aprendizaje&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sintaxis específica de ASL&lt;/td&gt;
&lt;td&gt;Tu lenguaje de programación&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Límite de payload&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;256 KB entre estados&lt;/td&gt;
&lt;td&gt;6 MB entre pasos&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;No es que una sea mejor que la otra. Es que cada una brilla en escenarios diferentes.&lt;/p&gt;
&lt;h3&gt;
  
  
  El límite de payload: un detalle que importa
&lt;/h3&gt;

&lt;p&gt;Hay algo que muchos descubren tarde: &lt;strong&gt;Step Functions tiene un límite de 256 KB&lt;/strong&gt; para los datos que pasan entre estados.&lt;/p&gt;

&lt;p&gt;Suena como mucho, hasta que no lo es.&lt;/p&gt;

&lt;p&gt;Imagina que estás procesando una imagen grande con metadata extensa. O generando múltiples variaciones con información de cada una. O trabajando con respuestas de APIs que devuelven arrays grandes de datos de procesamiento.&lt;/p&gt;

&lt;p&gt;De repente, ese límite de 256 KB se siente pequeño.&lt;/p&gt;

&lt;p&gt;La solución típica es guardar los datos en S3 y pasar solo la referencia:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"imageLocation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3://my-bucket/processed/image-123.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Funciona, pero agrega complejidad: más código, más servicios, más cosas que pueden fallar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Durable Functions tiene un límite mucho más generoso&lt;/strong&gt;: 6 MB entre pasos. Eso es 24 veces más espacio.&lt;/p&gt;

&lt;p&gt;No significa que debas pasar 6 MB de datos entre pasos (probablemente no deberías). Pero sí significa que tienes más margen antes de necesitar workarounds.&lt;/p&gt;

&lt;p&gt;Si tu workflow maneja datos grandes, esta diferencia puede ser decisiva.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cuándo usar cada una
&lt;/h2&gt;

&lt;p&gt;Aquí está la parte más importante del artículo. La pregunta no es "¿cuál es mejor?" sino "¿cuál necesito para este problema específico?"&lt;/p&gt;

&lt;h3&gt;
  
  
  Usa AWS Step Functions cuando:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Tu workflow tiene múltiples ramas condicionales (if/else complejos) → La visualización te ayuda a no perderte.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Necesitas que stakeholders no técnicos vean y entiendan el flujo → Abrir la consola de AWS es más fácil que explicar código.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;El workflow cambia frecuentemente → Ajustar JSON es más rápido que reescribir, testear y deployar código.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Quieres visualización nativa sin configuración adicional → AWS te da todo out-of-the-box.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Trabajas con servicios AWS que tienen integraciones directas → DynamoDB, SNS, SQS... Step Functions los conecta sin escribir código.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Usa Durable Functions cuando:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Tu workflow es principalmente secuencial con lógica compleja → El código es más natural que describir cada paso en JSON.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tu equipo prefiere trabajar completamente en código → IDE, tests unitarios, control de versiones... todo en un solo lugar.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Necesitas reutilizar lógica de negocio existente → Llamas funciones que ya tienes, sin adaptarlas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Quieres debugging tradicional con breakpoints → Step-through, inspección de variables, todo lo que ya conoces.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;El workflow requiere transformaciones de datos complejas entre pasos → Escribir esa lógica en código es más claro que en JSON.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Si después de leer esto sigues sin estar seguro... probablemente Step Functions es tu mejor opción. La visualización ayuda más de lo que crees cuando estás empezando.&lt;/p&gt;

&lt;p&gt;Y si quieres una forma rápida de recordarlo:&lt;/p&gt;

&lt;p&gt;📊 *&lt;em&gt;Árbol de decisión *&lt;/em&gt;&lt;br&gt;
Te dejo este árbol de decisión que puedes guardar o imprimir para tenerlo a mano cuando necesites elegir: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F69mrx54mdnaap3aizfpp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F69mrx54mdnaap3aizfpp.png" alt="arbol de decision" width="800" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  La verdad: se complementan
&lt;/h2&gt;

&lt;p&gt;Aquí está el secreto que casi nadie menciona: &lt;strong&gt;Puedes usar ambas en la misma arquitectura.&lt;/strong&gt; Imagina una arquitectura donde:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step Functions&lt;/strong&gt; orquesta el flujo principal de la aplicación:&lt;br&gt;&lt;br&gt;
→ Recibe la imagen&lt;br&gt;
→ Valida formato&lt;br&gt;
→ Procesa transformaciones&lt;br&gt;
→ Notifica al usuario&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Durable Functions&lt;/strong&gt; maneja sub-procesos complejos dentro de cada paso:&lt;br&gt;&lt;br&gt;
→ Aplicar múltiples filtros con lógica compleja&lt;br&gt;
→ Detección de contenido con reglas de negocio&lt;br&gt;
→ Transformaciones de metadata entre sistemas&lt;/p&gt;

&lt;p&gt;Step Functions te da la vista de 10,000 pies. Durable Functions te da el control fino donde lo necesitas. Algunos problemas se resuelven mejor con visualización. Otros se resuelven mejor con código.&lt;/p&gt;

&lt;p&gt;Y ahora tienes ambas opciones.&lt;/p&gt;

&lt;p&gt;Este es el primer artículo de una serie donde vamos a explorar Durable Functions en profundidad:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Construye tu primera AWS Lambda Durable Function&lt;/strong&gt;: Instalación, setup y tu primer workflow funcional paso a paso&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-step workflows que sobreviven fallos&lt;/strong&gt;: Cómo los checkpoints automáticos mantienen tu progreso incluso cuando algo truena&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Callbacks y aprobaciones externas&lt;/strong&gt;: Pausar workflows esperando input humano o eventos externos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Waits y condiciones&lt;/strong&gt;: Suspender ejecución por horas o días sin pagar por tiempo idle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ejecución en paralelo&lt;/strong&gt;: Correr múltiples pasos al mismo tiempo y coordinar resultados&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La pregunta no es "¿cuál va a sobrevivir?" Es "¿cuál necesito para este problema específico?" Y ahora ya sabes cómo responderla.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Qué viene después?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
En el próximo artículo, vamos a construir tu primera AWS Lambda Durable Function desde cero: instalación, setup y un workflow funcional paso a paso. Código real, problemas reales, soluciones reales.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;¿Te resultó útil este artículo?&lt;/strong&gt; Compártelo con tu equipo o déjame saber en los comentarios qué otros temas de serverless te gustaría que cubriera. Y si ya estás usando Step Functions o Durable Functions en producción, me encantaría escuchar tu experiencia.&lt;/p&gt;

</description>
      <category>lambda</category>
      <category>stepfunctions</category>
      <category>aws</category>
      <category>español</category>
    </item>
    <item>
      <title>Amazon API Gateway: La puerta de entrada a tu backend en la nube</title>
      <dc:creator>Hazel Saenz</dc:creator>
      <pubDate>Tue, 03 Mar 2026 14:57:58 +0000</pubDate>
      <link>https://dev.to/aws/amazon-api-gateway-la-puerta-de-entrada-a-tu-backend-en-la-nube-1bbi</link>
      <guid>https://dev.to/aws/amazon-api-gateway-la-puerta-de-entrada-a-tu-backend-en-la-nube-1bbi</guid>
      <description>&lt;p&gt;&lt;em&gt;Cómo API Gateway recibe requests y los conecta con tu lógica de backend.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Ya sabes &lt;a href="https://dev.to/aws/que-pasa-realmente-cuando-una-app-pide-algo-19g0"&gt;qué es una API y por qué las aplicaciones se comunican haciendo peticiones&lt;/a&gt;.&lt;br&gt;
Ya entiendes &lt;a href="https://dev.to/aws/a-donde-va-una-peticion-cuando-la-envias-y-que-pasa-en-el-camino-2bap"&gt;cómo una petición viaja desde tu app hasta el servidor&lt;/a&gt;.&lt;br&gt;
Ya viste &lt;a href="https://dev.to/aws/que-es-rest-y-como-responde-a-un-request-22ll"&gt;qué es REST y cómo el backend interpreta esa petición y decide qué responder&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pero quedó una pregunta sin responder:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;¿Cómo se implementa todo esto en la nube?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Porque una cosa es entender el concepto de una API REST, y otra muy distinta es desplegarla, gestionarla, escalarla, y mantenerla funcionando en producción.&lt;/p&gt;

&lt;p&gt;Ahí es donde entra &lt;a href="https://aws.amazon.com/api-gateway/" rel="noopener noreferrer"&gt;Amazon API Gateway&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;No como una tecnología complicada que tienes que dominar desde cero.   Sino como &lt;strong&gt;la puerta de entrada&lt;/strong&gt; que maneja todo lo que pasa antes de que tu lógica de backend se ejecute.&lt;/p&gt;

&lt;p&gt;Este artículo es para entender eso.&lt;/p&gt;
&lt;h2&gt;
  
  
  El problema que Amazon API Gateway resuelve
&lt;/h2&gt;

&lt;p&gt;Imagina que construiste una aplicación que devuelve información de personajes de Harry Potter. Tu aplicación funciona perfecto: recibe un ID, busca en la base de datos, devuelve el personaje.&lt;/p&gt;

&lt;p&gt;Pero ahora necesitas que tu app móvil pueda llamar a esa aplicación.&lt;/p&gt;

&lt;p&gt;¿Cómo lo haces?&lt;/p&gt;

&lt;p&gt;Podrías montar tu propio servidor. De hecho, eso es lo que los equipos hacían antes. Configuraban servidores con &lt;strong&gt;Nginx o Apache&lt;/strong&gt;, instalaban &lt;strong&gt;load balancers&lt;/strong&gt;, escribían reglas de enrutamiento, configuraban SSL manualmente, y monitoreaban logs constantemente.&lt;/p&gt;

&lt;p&gt;Todo esto funcionaba. Pero requería experiencia en DevOps, mantener servidores 24/7, pagar por ellos aunque nadie los usara, y escalar manualmente cuando cambiaba el tráfico.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/api-gateway/" rel="noopener noreferrer"&gt;Amazon API Gateway&lt;/a&gt; elimina toda esa complejidad.&lt;/p&gt;

&lt;p&gt;Es un servicio administrado que actúa como la puerta de entrada a tu backend. No es tu backend, no es donde vive tu lógica, no es donde guardas tus datos. Es &lt;strong&gt;el punto de entrada&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Piensa en API Gateway como el recepcionista de un edificio: recibe a todos los visitantes, verifica que tengan permiso, los dirige al piso correcto, pero no hace el trabajo de los que están en las oficinas.&lt;/p&gt;

&lt;p&gt;No tienes que mantener servidores. No pagas cuando no hay tráfico. Escala automáticamente. Maneja HTTPS, configuración de seguridad para navegadores web, autenticación, y límites de peticiones por ti.&lt;/p&gt;

&lt;p&gt;Es como tener un equipo de DevOps administrando tu puerta de entrada, pero sin tener que contratar a nadie.&lt;/p&gt;

&lt;p&gt;Y lo más importante: &lt;strong&gt;te deja enfocarte en tu lógica de negocio&lt;/strong&gt;, no en la infraestructura.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cómo funciona el flujo básico
&lt;/h2&gt;

&lt;p&gt;Cuando tu app hace una petición a una API desplegada con API Gateway:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;La petición llega a API Gateway&lt;/strong&gt; - Tu app hace un &lt;code&gt;GET https://api.tuapp.com/personajes/8&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway valida&lt;/strong&gt; - Verifica que la petición esté bien formada y tenga permisos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Llama a tu backend&lt;/strong&gt; - En este caso, ejecuta tu función Lambda&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Devuelve la respuesta&lt;/strong&gt; - Tu app recibe el JSON con la información del personaje&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Todo ese proceso pasa en milisegundos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgu65g998iiipakok35dy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgu65g998iiipakok35dy.png" alt="API Gateway Flow" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Construyamos nuestra primera API con API Gateway + Lambda
&lt;/h2&gt;

&lt;p&gt;Vamos a construir una API simple que devuelve información de un personaje de Harry Potter.&lt;/p&gt;

&lt;p&gt;Para esto usaremos dos servicios de AWS:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt;&lt;/strong&gt; es un servicio que ejecuta tu código sin que tengas que administrar servidores. Tú solo subes tu código y Lambda se encarga de ejecutarlo cuando lo necesites. Aquí es donde vivirá nuestra lógica de negocio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Gateway&lt;/strong&gt; será la puerta de entrada que recibe las peticiones HTTP y las conecta con nuestra función Lambda.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1x1z9to3um5fa3ygg29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1x1z9to3um5fa3ygg29.png" alt="API Gateway Demo" width="800" height="336"&gt;&lt;/a&gt;&lt;br&gt;
No te preocupes si es tu primera vez usando AWS. Vamos paso a paso.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nota importante&lt;/strong&gt;: Este tutorial puede realizarse completamente dentro de la &lt;a href="https://aws.amazon.com/free/" rel="noopener noreferrer"&gt;capa gratuita de AWS&lt;/a&gt;, así que no tendrá ningún costo si tu cuenta es nueva o si no has excedido los límites gratuitos. Al final del artículo te mostraré cómo limpiar los recursos para evitar que se consuman tus créditos gratuitos (no se hará ningún cargo a tu tarjeta).&lt;/p&gt;
&lt;h3&gt;
  
  
  Paso 1: Crear la función Lambda
&lt;/h3&gt;

&lt;p&gt;Primero necesitamos crear la función que contendrá nuestra lógica.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crear la función:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ve a la consola de AWS y busca "Lambda" en el buscador superior&lt;/li&gt;
&lt;li&gt;Haz clic en "Create function" (Crear función)&lt;/li&gt;
&lt;li&gt;Selecciona "Author from scratch" (Crear desde cero)&lt;/li&gt;
&lt;li&gt;Configura tu función:

&lt;ul&gt;
&lt;li&gt;Function name: &lt;code&gt;obtenerPersonaje&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Runtime: Node.js 20.x (o la versión más reciente)&lt;/li&gt;
&lt;li&gt;Architecture: x86_64&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Deja las demás opciones por defecto y haz clic en "Create function"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hazelsaenz.tech%2FArticlesMedia%2FNewLambda.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.hazelsaenz.tech%2FArticlesMedia%2FNewLambda.gif" alt="Lambda configuration - Function name and runtime" width="755" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agregar el código:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Una vez creada, verás el editor de código. Reemplaza el código por defecto con este:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Base de datos simulada de personajes&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;personajes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;nombre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Harry Potter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;casa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Gryffindor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;poder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;95&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;nombre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hermione Granger&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;casa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Gryffindor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;poder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;98&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;nombre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ron Weasley&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;casa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Gryffindor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;poder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;85&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;nombre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ginny Weasley&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;casa&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Gryffindor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;poder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;83&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// Obtener el ID del personaje desde la petición&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ID recibido:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Buscar el personaje&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;personaje&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;personajes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// Si no existe, devolver error&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;personaje&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Personaje no encontrado&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Devolver el personaje encontrado&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;personaje&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Haz clic en "Deploy" para guardar los cambios&lt;/li&gt;
&lt;/ol&gt;

   

&lt;p&gt;Tu función Lambda ya está lista. Ahora vamos a conectarla con API Gateway.&lt;/p&gt;
&lt;h3&gt;
  
  
  Paso 2: Crear la API en API Gateway
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Ve a la consola de AWS y busca "API Gateway"&lt;/li&gt;
&lt;li&gt;Haz clic en "Create API" (Crear API)&lt;/li&gt;
&lt;li&gt;Selecciona "REST API" (no la opción Private)&lt;/li&gt;
&lt;li&gt;Haz clic en "Build" (Crear)&lt;/li&gt;
&lt;li&gt;Configura tu API:

&lt;ul&gt;
&lt;li&gt;Choose the protocol: REST&lt;/li&gt;
&lt;li&gt;Create new API: New API&lt;/li&gt;
&lt;li&gt;API name: &lt;code&gt;API-Personajes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Description: API para obtener información de personajes&lt;/li&gt;
&lt;li&gt;Endpoint Type: Regional&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Haz clic en "Create API"&lt;/li&gt;
&lt;/ol&gt;

   
&lt;h3&gt;
  
  
  Paso 3: Crear el recurso y método
&lt;/h3&gt;

&lt;p&gt;Ahora vamos a definir la estructura de nuestra API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crear el recurso principal:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;En el panel izquierdo, verás tu API. Haz clic en "Create Resource" (Crear recurso)&lt;/li&gt;
&lt;li&gt;Configura el recurso:

&lt;ul&gt;
&lt;li&gt;Resource Name: &lt;code&gt;personajes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Resource Path: &lt;code&gt;/personajes&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Marca la opción "Enable API Gateway CORS" (esto permite que tu API sea llamada desde navegadores)&lt;/li&gt;
&lt;li&gt;Haz clic en "Create Resource"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Crear el sub-recurso con parámetro:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Con el recurso &lt;code&gt;/personajes&lt;/code&gt; seleccionado, haz clic en "Create Resource" nuevamente para crear un sub-recurso&lt;/li&gt;
&lt;li&gt;Configura:

&lt;ul&gt;
&lt;li&gt;Resource Name: &lt;code&gt;{id}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Resource Path: &lt;code&gt;/{id}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Marca "Enable API Gateway CORS"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Haz clic en "Create Resource"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ahora tienes la ruta &lt;code&gt;/personajes/{id}&lt;/code&gt; donde &lt;code&gt;{id}&lt;/code&gt; es un parámetro dinámico.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crear el método GET:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Con el recurso &lt;code&gt;{id}&lt;/code&gt; seleccionado, haz clic en "Create Method" (Crear método)&lt;/li&gt;
&lt;li&gt;Selecciona "GET" del dropdown y haz clic en el check ✓&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Configurar la integración con Lambda:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Configura la integración:

&lt;ul&gt;
&lt;li&gt;Integration type: Lambda Function&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IMPORTANTE: Marca la casilla "Use Lambda Proxy integration"&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Lambda Region: (tu región, por ejemplo us-east-1)&lt;/li&gt;
&lt;li&gt;Lambda Function: &lt;code&gt;obtenerPersonaje&lt;/code&gt; (empieza a escribir y aparecerá)&lt;/li&gt;
&lt;li&gt;Deja las demás opciones por defecto&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Haz clic en "Save"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Nota importante sobre Lambda Proxy Integration&lt;/strong&gt;: Esta opción le dice a API Gateway que pase toda la información de la petición HTTP (headers, path parameters, query strings, body) directamente a tu función Lambda en el objeto &lt;code&gt;event&lt;/code&gt;. Sin esta opción, tendrías que configurar manualmente cómo se mapean los parámetros, lo cual es más complejo y propenso a errores.&lt;/p&gt;

   

&lt;ol&gt;
&lt;li&gt;Aparecerá un mensaje pidiendo permiso para que API Gateway invoque tu Lambda. Haz clic en "OK"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Este paso es importante: le estás dando permiso a API Gateway para que pueda ejecutar tu función Lambda.&lt;br&gt;&lt;br&gt;
Sin este permiso, API Gateway no podría llamar a tu función cuando llegue una petición.&lt;br&gt;&lt;br&gt;
AWS maneja esto automáticamente por ti cuando haces clic en "OK".&lt;/p&gt;
&lt;h3&gt;
  
  
  Paso 4: Desplegar la API
&lt;/h3&gt;

&lt;p&gt;Tu API está configurada, pero aún no está disponible públicamente. Necesitas desplegarla.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Haz clic en "Deploy API" en el menú superior&lt;/li&gt;
&lt;li&gt;Configura el despliegue:

&lt;ul&gt;
&lt;li&gt;Deployment stage: [New Stage]&lt;/li&gt;
&lt;li&gt;Stage name: &lt;code&gt;prod&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Stage description: Producción&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Haz clic en "Deploy"&lt;/li&gt;
&lt;/ol&gt;

   

&lt;p&gt;¡Listo! Tu API ya está en producción.&lt;/p&gt;
&lt;h3&gt;
  
  
  Paso 5: Probar tu API
&lt;/h3&gt;

&lt;p&gt;API Gateway te da una URL pública para tu API.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;En la pantalla de "Stages", verás tu stage "prod"&lt;/li&gt;
&lt;li&gt;Expande el árbol: prod → /personajes → /{id} → GET&lt;/li&gt;
&lt;li&gt;En la parte superior verás la "Invoke URL"
La URL se verá algo así:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://abc123xyz.execute-api.us-east-1.amazonaws.com/prod/personajes/8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Copia esa URL y ábrela en tu navegador (cambia el &lt;code&gt;8&lt;/code&gt; por &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;, &lt;code&gt;3&lt;/code&gt;, u &lt;code&gt;8&lt;/code&gt; para ver diferentes personajes)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;También puedes probarla desde la terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://tu-url-aqui.execute-api.us-east-1.amazonaws.com/prod/personajes/8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deberías ver una respuesta como esta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nombre"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ginny Weasley"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"casa"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Gryffindor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"poder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0i26umk6ckgp3iwy2g97.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0i26umk6ckgp3iwy2g97.png" alt="curl JSON response" width="800" height="97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué acabas de hacer?
&lt;/h3&gt;

&lt;p&gt;Acabas de construir una API REST completa en la nube:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creaste una función Lambda con tu lógica de negocio&lt;/li&gt;
&lt;li&gt;Configuraste API Gateway como punto de entrada&lt;/li&gt;
&lt;li&gt;Definiste una ruta con parámetros dinámicos (&lt;code&gt;/personajes/{id}&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Desplegaste tu API en producción&lt;/li&gt;
&lt;li&gt;Obtuviste una URL pública que cualquiera puede llamar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Todo sin:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Montar servidores&lt;/li&gt;
&lt;li&gt;Configurar balanceadores de carga&lt;/li&gt;
&lt;li&gt;Manejar certificados SSL&lt;/li&gt;
&lt;li&gt;Escribir código de infraestructura&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eso es el poder de serverless + API Gateway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limpieza: Eliminar los recursos
&lt;/h3&gt;

&lt;p&gt;Si solo estás probando y no quieres que estos recursos sigan activos en tu cuenta de AWS, es importante eliminarlos para evitar que se consuman tus créditos gratuitos (si estás en la capa gratuita) o para evitar cargos innecesarios.&lt;/p&gt;

&lt;h4&gt;
  
  
  Eliminar la API en API Gateway
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Ve a la consola de API Gateway&lt;/li&gt;
&lt;li&gt;Selecciona tu API &lt;code&gt;API-Personajes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Haz clic en "Actions" → "Delete API"&lt;/li&gt;
&lt;li&gt;Confirma escribiendo el nombre de la API y haz clic en "Delete API"&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Eliminar la función Lambda
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Ve a la consola de Lambda&lt;/li&gt;
&lt;li&gt;Selecciona tu función &lt;code&gt;obtenerPersonaje&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Haz clic en "Actions" → "Delete"&lt;/li&gt;
&lt;li&gt;Confirma la eliminación&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Con esto, todos los recursos del tutorial están eliminados y no generarán cargos en tu cuenta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué esto importa para arquitecturas serverless
&lt;/h2&gt;

&lt;p&gt;Porque en serverless, tus funciones no están "corriendo" todo el tiempo. Se ejecutan cuando las llamas. Y se apagan cuando terminan. Pero tu app necesita una URL estable. Un punto de entrada que siempre esté disponible. API Gateway es ese punto de entrada.&lt;/p&gt;

&lt;p&gt;Tu app siempre llama a la misma URL. API Gateway siempre está ahí. Y cuando llega una petición, despierta tu Lambda, la ejecuta, y devuelve la respuesta.&lt;/p&gt;

&lt;p&gt;Sin API Gateway, serverless no funcionaría para APIs públicas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que aprendiste
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;API Gateway es la puerta de entrada, no el backend.
&lt;/li&gt;
&lt;li&gt;Recibe peticiones HTTP y las conecta con tu lógica real.
&lt;/li&gt;
&lt;li&gt;Maneja validación, transformación, seguridad, y throttling.
&lt;/li&gt;
&lt;li&gt;Puede integrarse con Lambda, HTTP endpoints, o servicios de AWS.
&lt;/li&gt;
&lt;li&gt;Incluye características de gestión de APIs sin código adicional.
&lt;/li&gt;
&lt;li&gt;Es esencial para arquitecturas serverless.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No es magia. Es infraestructura que alguien más mantiene por ti.&lt;/p&gt;

&lt;p&gt;Y esa diferencia es lo que te permite construir APIs en producción sin tener que ser experto en DevOps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;API Gateway es la puerta de entrada, no el backend.
&lt;/li&gt;
&lt;li&gt;Recibe peticiones HTTP y las conecta con tu lógica real.
&lt;/li&gt;
&lt;li&gt;Maneja validación, transformación, seguridad, y límites de peticiones.
&lt;/li&gt;
&lt;li&gt;Puede integrarse con Lambda, HTTP endpoints, o servicios de AWS.
&lt;/li&gt;
&lt;li&gt;Incluye características de gestión de APIs sin código adicional.
&lt;/li&gt;
&lt;li&gt;Es esencial para arquitecturas serverless.
Ahora tienes las bases para construir tus propias APIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No porque hayas memorizado sintaxis o comandos.&lt;br&gt;&lt;br&gt;
Sino porque entiendes &lt;strong&gt;cómo funciona la conversación&lt;/strong&gt; de principio a fin.&lt;/p&gt;

&lt;p&gt;Y esa claridad es lo que te permite construir mejor, debuggear más rápido, y diseñar con más confianza.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>api</category>
      <category>apigateway</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Qué es REST y cómo responde a un request</title>
      <dc:creator>Hazel Saenz</dc:creator>
      <pubDate>Tue, 24 Feb 2026 20:52:08 +0000</pubDate>
      <link>https://dev.to/aws/que-es-rest-y-como-responde-a-un-request-22ll</link>
      <guid>https://dev.to/aws/que-es-rest-y-como-responde-a-un-request-22ll</guid>
      <description>&lt;p&gt;Una explicación simple para entender cómo el backend interpreta lo que pides y decide qué responderte.&lt;/p&gt;




&lt;p&gt;Ya sabes que las aplicaciones hacen peticiones, ya sabes que esas peticiones viajan por la red hasta llegar a un servidor, pero quedó una pregunta sin responder:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;¿Qué pasa cuando la petición llega? ¿Cómo sabe el servidor qué hacer con ella?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Esa es la parte que casi nunca se explica bien. Y es justo ahí donde entra REST.&lt;/p&gt;

&lt;p&gt;No como una tecnología mágica, no como un framework que instalas. Sino como &lt;strong&gt;una forma de organizar la conversación&lt;/strong&gt; entre quien pide y quien responde.&lt;/p&gt;

&lt;p&gt;Este artículo es para entender eso.&lt;/p&gt;

&lt;h2&gt;
  
  
  La petición llegó. ¿Y ahora qué?
&lt;/h2&gt;

&lt;p&gt;En el &lt;a href="https://dev.to/aws/a-donde-va-una-peticion-cuando-la-envias-y-que-pasa-en-el-camino-2bap"&gt;artículo anterior&lt;/a&gt; vimos el viaje completo de una petición: desde que sale de tu app, viaja por la red, llega al servidor, y regresa con una respuesta.&lt;/p&gt;

&lt;p&gt;Pero quedó pendiente entender qué pasa exactamente cuando esa petición llega al servidor.&lt;/p&gt;

&lt;p&gt;Imagina que tu app envía esta petición:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /personajes/8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El servidor la recibe. La lee. Y tiene que decidir:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;¿Qué está pidiendo?
&lt;/li&gt;
&lt;li&gt;¿Qué debería responder?
&lt;/li&gt;
&lt;li&gt;¿Cómo debería estructurar esa respuesta?
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si no hay reglas claras, cada servidor podría interpretar eso de forma diferente.&lt;/p&gt;

&lt;p&gt;Uno podría pensar que &lt;code&gt;/personajes/8&lt;/code&gt; significa "dame el personaje con ID 8". &lt;br&gt;
Otro podría pensar que significa "borra el personaje 8".&lt;br&gt;
Otro podría no entender nada.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;REST existe para evitar ese caos.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Define un conjunto de reglas que hacen que la conversación sea predecible.&lt;/p&gt;
&lt;h2&gt;
  
  
  REST no es una tecnología
&lt;/h2&gt;

&lt;p&gt;Aquí es donde empieza la confusión.&lt;/p&gt;

&lt;p&gt;REST no es:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un lenguaje de programación
&lt;/li&gt;
&lt;li&gt;Un framework que instalas
&lt;/li&gt;
&lt;li&gt;Una librería que importas
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;REST es &lt;strong&gt;un estilo de comunicación&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Un acuerdo sobre cómo estructurar las peticiones y las respuestas para que ambas partes se entiendan sin necesidad de conocerse por dentro.&lt;/p&gt;

&lt;p&gt;Piensa en REST como las reglas de una conversación educada:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cuando preguntas algo, lo haces de forma clara
&lt;/li&gt;
&lt;li&gt;Cuando respondes, das información útil
&lt;/li&gt;
&lt;li&gt;Ambas partes saben qué esperar del otro
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eso es REST. Nada más. Nada menos.&lt;/p&gt;
&lt;h2&gt;
  
  
  Las peticiones expresan intención
&lt;/h2&gt;

&lt;p&gt;En REST, cada petición tiene dos partes clave:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;El recurso&lt;/strong&gt; (qué estás pidiendo)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;El verbo HTTP&lt;/strong&gt; (qué quieres hacer con ese recurso)
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Volviendo al ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /personajes/8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Recurso&lt;/strong&gt;: &lt;code&gt;/personajes/8&lt;/code&gt; (el personaje con ID 8)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verbo&lt;/strong&gt;: &lt;code&gt;GET&lt;/code&gt; (quiero obtener información)
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El verbo no es un comando. Es una &lt;strong&gt;intención&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No le estás diciendo al servidor "ejecuta esta función". Le estás diciendo "esto es lo que quiero hacer". Y el servidor decide cómo hacerlo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Los verbos HTTP como lenguaje
&lt;/h2&gt;

&lt;p&gt;REST usa los verbos HTTP para expresar intenciones comunes:&lt;/p&gt;

&lt;h3&gt;
  
  
  GET: "Quiero ver algo"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /personajes/8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Dame información del personaje 8"&lt;/p&gt;

&lt;p&gt;No modifica nada. Solo pide información.&lt;/p&gt;

&lt;h3&gt;
  
  
  POST: "Quiero crear algo nuevo"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /personajes
{
  "nombre": "Luna Lovegood",
  "casa": "Ravenclaw"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Crea un nuevo personaje con esta información"&lt;/p&gt;

&lt;h3&gt;
  
  
  PUT/PATCH: "Quiero actualizar algo"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PATCH /personajes/8
{
  "poder": 90
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Actualiza el poder del personaje 8"&lt;/p&gt;

&lt;h3&gt;
  
  
  DELETE: "Quiero eliminar algo"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DELETE /personajes/8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Elimina el personaje 8"&lt;/p&gt;

&lt;p&gt;No tienes que memorizar esto. Solo tienes que entender que &lt;strong&gt;cada verbo expresa una intención diferente&lt;/strong&gt;. Y REST usa esas intenciones para organizar la conversación.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recursos, no acciones
&lt;/h2&gt;

&lt;p&gt;Aquí hay algo importante que muchas veces se pasa por alto: En REST, las URLs representan &lt;strong&gt;recursos&lt;/strong&gt;, no acciones.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Mal diseño&lt;/strong&gt; (acciones en la URL):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /crearPersonaje
POST /eliminarPersonaje
POST /actualizarPersonaje
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Buen diseño&lt;/strong&gt; (recursos + verbos):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /personajes
DELETE /personajes/8
PATCH /personajes/8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;¿Ves la diferencia?&lt;/p&gt;

&lt;p&gt;En el primer caso, la URL dice &lt;strong&gt;qué hacer&lt;/strong&gt;. En el segundo caso, la URL dice &lt;strong&gt;sobre qué&lt;/strong&gt; y el verbo dice &lt;strong&gt;qué hacer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Esa separación hace que las APIs sean más claras y predecibles.&lt;/p&gt;

&lt;h2&gt;
  
  
  El servidor interpreta y decide
&lt;/h2&gt;

&lt;p&gt;Cuando el servidor recibe una petición REST, sigue un proceso:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Lee el verbo&lt;/strong&gt;: ¿Qué quiere hacer?
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lee el recurso&lt;/strong&gt;: ¿Sobre qué?
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evalúa&lt;/strong&gt;: ¿Tiene permiso? ¿Existe ese recurso?
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ejecuta la lógica&lt;/strong&gt;: Busca en la base de datos, procesa, valida
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Construye la respuesta&lt;/strong&gt;: Decide qué devolver&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7dw55lqh7044xe8zztrh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7dw55lqh7044xe8zztrh.png" alt="RESTFlow" width="800" height="103"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Por ejemplo, si recibe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /personajes/8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El servidor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identifica que es una petición GET (quiere información)
&lt;/li&gt;
&lt;li&gt;Identifica que el recurso es &lt;code&gt;/personajes/8&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Busca en la base de datos el personaje con ID 8
&lt;/li&gt;
&lt;li&gt;Si lo encuentra, lo devuelve
&lt;/li&gt;
&lt;li&gt;Si no lo encuentra, responde con un error
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Todo eso pasa en el backend.&lt;/p&gt;

&lt;p&gt;La app que hizo la petición &lt;strong&gt;no sabe cómo&lt;/strong&gt; se hizo.&lt;br&gt;&lt;br&gt;
Solo sabe que si pide de esa forma, va a recibir lo que necesita.&lt;/p&gt;
&lt;h2&gt;
  
  
  Las respuestas tienen significado
&lt;/h2&gt;

&lt;p&gt;En REST, las respuestas no son solo datos. Son &lt;strong&gt;decisiones estructuradas&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Cada respuesta incluye:&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Un código de estado
&lt;/h3&gt;

&lt;p&gt;Que dice &lt;strong&gt;qué pasó&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;200 OK&lt;/code&gt;: Todo bien, aquí está lo que pediste
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;201 Created&lt;/code&gt;: Se creó algo nuevo
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;404 Not Found&lt;/code&gt;: No encontré lo que pediste
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;400 Bad Request&lt;/code&gt;: Tu petición está mal formada
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;500 Internal Server Error&lt;/code&gt;: Algo falló de mi lado
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Estos son solo algunos de los códigos más comunes. Los códigos de estado HTTP son estándar, pero cada tipo de servidor puede manejar y devolver errores de forma ligeramente diferente. Puedes ver la &lt;a href="https://developer.mozilla.org/es/docs/Web/HTTP/Status" rel="noopener noreferrer"&gt;lista completa de códigos de estado HTTP&lt;/a&gt; en la documentación de MDN.  &lt;/p&gt;
&lt;h3&gt;
  
  
  2. Un cuerpo de respuesta
&lt;/h3&gt;

&lt;p&gt;Que contiene &lt;strong&gt;la información&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nombre"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ginny Weasley"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"casa"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Gryffindor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"poder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Headers (opcional)
&lt;/h3&gt;

&lt;p&gt;Que dan &lt;strong&gt;contexto adicional&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Content-Type: application/json
Cache-Control: max-age=3600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por ejemplo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Content-Type: application/json&lt;/code&gt; indica que la respuesta está en formato JSON&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Cache-Control: max-age=3600&lt;/code&gt; indica que la respuesta puede guardarse en caché por 1 hora&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Todo eso junto forma una respuesta completa. No es solo "aquí están los datos". Es "esto es lo que pasó, esto es lo que encontré, y así deberías interpretarlo".&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué REST reduce la confusión
&lt;/h2&gt;

&lt;p&gt;Porque cuando todos siguen las mismas reglas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Las peticiones son predecibles
&lt;/li&gt;
&lt;li&gt;Las respuestas tienen estructura
&lt;/li&gt;
&lt;li&gt;Los errores tienen significado
&lt;/li&gt;
&lt;li&gt;No tienes que adivinar cómo funciona cada API
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sin REST (o sin algún estilo similar), cada API sería diferente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unos usarían POST para todo
&lt;/li&gt;
&lt;li&gt;Otros inventarían sus propios códigos de error
&lt;/li&gt;
&lt;li&gt;Otros mezclarían acciones y recursos sin criterio
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;REST no es perfecto. Pero es un acuerdo que funciona.&lt;/p&gt;

&lt;p&gt;Y cuando lo entiendes, dejas de ver las APIs como algo mágico y empiezas a verlas como lo que son: &lt;strong&gt;conversaciones con reglas claras&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que aprendiste
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;REST es un estilo de comunicación, no una tecnología.&lt;/li&gt;
&lt;li&gt;Las peticiones expresan &lt;strong&gt;intención&lt;/strong&gt; a través de verbos HTTP.&lt;/li&gt;
&lt;li&gt;Los recursos representan &lt;strong&gt;qué&lt;/strong&gt;, no &lt;strong&gt;cómo&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;El servidor &lt;strong&gt;interpreta&lt;/strong&gt; la petición y &lt;strong&gt;decide&lt;/strong&gt; qué responder.&lt;/li&gt;
&lt;li&gt;Las respuestas tienen &lt;strong&gt;significado&lt;/strong&gt;, no solo datos.&lt;/li&gt;
&lt;li&gt;REST reduce el caos al establecer reglas claras para la conversación.&lt;/li&gt;
&lt;li&gt;Entender REST no es memorizar verbos. Es entender &lt;strong&gt;por qué&lt;/strong&gt; las APIs se diseñan de cierta forma. Y esa claridad es lo que te permite construir mejor, debuggear más rápido, y diseñar con más confianza.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;Ya entiendes qué es REST y cómo organiza la conversación entre quien pide y quien responde.&lt;/p&gt;

&lt;p&gt;En el siguiente artículo vamos a ver &lt;strong&gt;cómo se implementa esto en la nube&lt;/strong&gt; usando &lt;a href="https://aws.amazon.com/api-gateway/" rel="noopener noreferrer"&gt;Amazon API Gateway&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vas a entender cómo API Gateway actúa como la puerta de entrada a tu backend, cómo recibe peticiones REST, y cómo las conecta con tu lógica (ya sea en Lambda, en un servidor HTTP, o en otros servicios de AWS).&lt;/p&gt;

&lt;p&gt;Ya sabes cómo funciona la conversación. Ahora es momento de ver cómo se despliega en producción.&lt;/p&gt;

</description>
      <category>api</category>
      <category>programming</category>
      <category>espanol</category>
      <category>development</category>
    </item>
    <item>
      <title>A dónde va una petición cuando la envías (y qué pasa en el camino)</title>
      <dc:creator>Hazel Saenz</dc:creator>
      <pubDate>Tue, 17 Feb 2026 14:35:30 +0000</pubDate>
      <link>https://dev.to/aws/a-donde-va-una-peticion-cuando-la-envias-y-que-pasa-en-el-camino-2bap</link>
      <guid>https://dev.to/aws/a-donde-va-una-peticion-cuando-la-envias-y-que-pasa-en-el-camino-2bap</guid>
      <description>&lt;p&gt;&lt;em&gt;Una explicación simple del viaje completo: desde que tu app pide algo hasta que recibe respuesta.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Ya sabes que las aplicaciones se comunican haciendo peticiones.&lt;/p&gt;

&lt;p&gt;Tocas un botón. ➡️ La app pide algo. ➡️ Algo responde.&lt;/p&gt;

&lt;p&gt;Pero entre ese "pedir" y ese "responder" hay un viaje completo. Y casi nunca se explica.&lt;/p&gt;

&lt;p&gt;La mayoría de las veces, cuando aprendes sobre APIs, te dicen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"La app hace una petición"
&lt;/li&gt;
&lt;li&gt;"El servidor responde"
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Y listo.&lt;/p&gt;

&lt;p&gt;Como si todo pasara instantáneamente. Como si no hubiera nada en el medio. Pero sí lo hay.&lt;/p&gt;

&lt;p&gt;Y entender ese recorrido es lo que te ayuda a dejar de ver las APIs como algo abstracto y empezar a verlas como lo que son: &lt;strong&gt;conversaciones con pasos claros&lt;/strong&gt;. Este artículo es ese mapa.&lt;/p&gt;

&lt;p&gt;En el &lt;a href="https://dev.to/aws/que-pasa-realmente-cuando-una-app-pide-algo-19g0"&gt;artículo anterior&lt;/a&gt; vimos que las aplicaciones no guardan todo localmente.&lt;/p&gt;

&lt;p&gt;Cuando necesitan algo, &lt;strong&gt;preguntan&lt;/strong&gt;, hacen una petición y esperan una respuesta.&lt;/p&gt;

&lt;p&gt;Eso es una API: el acuerdo que define cómo se hace esa conversación. Pero quedó una pregunta sin responder:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;¿Qué pasa entre el momento en que la app envía la petición y el momento en que recibe la respuesta?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Eso es lo que vamos a ver ahora.&lt;/p&gt;

&lt;h2&gt;
  
  
  Una petición sale de la app
&lt;/h2&gt;

&lt;p&gt;Imagina que abres una app de Harry Potter y buscas información sobre "Ginny Weasley". (Y sí, si me conoces sabes que soy fan de Harry Potter, así que  este ejemplo me hace especialmente feliz 😊)&lt;/p&gt;

&lt;p&gt;La app no tiene todos los datos de los personajes guardados en tu teléfono. Así que hace una petición:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /personajes?nombre=Ginny+Weasley
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esa petición es un &lt;strong&gt;mensaje&lt;/strong&gt;, no es código que se ejecuta en otro lugar, no es un archivo que se envía, es simplemente información estructurada que dice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Qué quiero&lt;/strong&gt;: información de personajes
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sobre quién&lt;/strong&gt;: Ginny Weasley
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cómo lo pido&lt;/strong&gt;: usando el método GET
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ese mensaje sale de tu app. Y empieza su viaje.➡️ ✉️ &lt;/p&gt;

&lt;h2&gt;
  
  
  El viaje a través de la red
&lt;/h2&gt;

&lt;p&gt;Tu petición no llega directamente al servidor, pasa por varios puntos en el camino:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sale de tu dispositivo&lt;/strong&gt; (tu teléfono, tu computadora)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pasa por tu red local&lt;/strong&gt; (tu WiFi o datos móviles)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Viaja por internet&lt;/strong&gt; (a través de routers, cables, infraestructura)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Llega al servidor&lt;/strong&gt; que tiene la información
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5mzin8goyly1hb31igfe.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5mzin8goyly1hb31igfe.gif" alt="Request" width="720" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cada uno de esos pasos toma tiempo, a veces milisegundos y a veces segundos; por eso a veces una app carga rápido y otras veces tarda, no siempre es culpa de la app. A veces es el camino.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://hsaenzg.github.io/api_call/" rel="noopener noreferrer"&gt;Prueba este ejemplo interactivo&lt;/a&gt;&lt;/strong&gt; para ver en acción cómo funciona una petición a una API de Harry Potter. Puedes ver el &lt;a href="https://github.com/hsaenzG/api_call.git" rel="noopener noreferrer"&gt;código de la demo en GitHub&lt;/a&gt; para entender cómo está construida.&lt;/p&gt;

&lt;h2&gt;
  
  
  El servidor recibe la petición
&lt;/h2&gt;

&lt;p&gt;Cuando la petición llega al servidor, no se ejecuta automáticamente.&lt;/p&gt;

&lt;p&gt;El servidor la &lt;strong&gt;recibe&lt;/strong&gt; y la &lt;strong&gt;evalúa&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Piensa en el servidor como un portero que revisa cada mensaje que llega:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;¿Esta petición está bien formada?
&lt;/li&gt;
&lt;li&gt;¿Tiene permiso para pedir esto?
&lt;/li&gt;
&lt;li&gt;¿Entiendo lo que está pidiendo?
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si algo falla en esta etapa, el servidor responde con un error.&lt;/p&gt;

&lt;p&gt;Por ejemplo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Si pediste algo que no existe: &lt;code&gt;404 Not Found&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Si no tienes permiso: &lt;code&gt;403 Forbidden&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Si la petición está mal hecha: &lt;code&gt;400 Bad Request&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pero si todo está bien, el servidor pasa a la siguiente etapa.&lt;/p&gt;

&lt;h2&gt;
  
  
  El proceso de decisión
&lt;/h2&gt;

&lt;p&gt;Aquí es donde pasa la magia. El servidor no solo devuelve información, &lt;strong&gt;decide&lt;/strong&gt; qué devolver.&lt;/p&gt;

&lt;p&gt;Siguiendo el ejemplo de la app de Harry Potter:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;El servidor recibe: &lt;code&gt;GET /personajes?nombre=Ginny+Weasley&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Busca en su base de datos información sobre "Ginny Weasley"
&lt;/li&gt;
&lt;li&gt;Recopila todos los datos del personaje (casa, varita, patronus, etc.)
&lt;/li&gt;
&lt;li&gt;Puede que incluya información relacionada (amigos, hechizos favoritos)
&lt;/li&gt;
&lt;li&gt;Procesa esa información
&lt;/li&gt;
&lt;li&gt;La estructura en el formato acordado (generalmente JSON)
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Todo eso pasa en el backend.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;La app que hizo la petición &lt;strong&gt;no sabe&lt;/strong&gt; cómo funciona ese proceso.
&lt;/li&gt;
&lt;li&gt;No sabe si el servidor usa una base de datos SQL o NoSQL.
&lt;/li&gt;
&lt;li&gt;No sabe cómo funciona el algoritmo de recomendación.
&lt;/li&gt;
&lt;li&gt;No sabe si está en un servidor en California o en Singapur.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Solo sabe que si pide de cierta forma, va a recibir lo que necesita.&lt;/p&gt;

&lt;p&gt;Esa es la belleza de las APIs: &lt;strong&gt;separan el "qué" del "cómo"&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  La respuesta regresa
&lt;/h2&gt;

&lt;p&gt;Una vez que el servidor termina de procesar, construye una respuesta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"nombre"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ginny Weasley"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"casa"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Gryffindor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"poder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hechizo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Reducto"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"defensa"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;77&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"imagen"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"🧙‍♀️"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esa respuesta hace el viaje de regreso:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sale del servidor
&lt;/li&gt;
&lt;li&gt;Viaja por internet
&lt;/li&gt;
&lt;li&gt;Pasa por tu red local
&lt;/li&gt;
&lt;li&gt;Llega a tu dispositivo
&lt;/li&gt;
&lt;li&gt;Tu app la recibe
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;El mismo camino, pero en reversa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cómo reacciona la app
&lt;/h2&gt;

&lt;p&gt;Cuando la app recibe la respuesta, &lt;strong&gt;reacciona&lt;/strong&gt;, no decide, no procesa lógica compleja, solo toma lo que recibió y lo muestra.&lt;/p&gt;

&lt;p&gt;En este caso:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Muestra la foto del personaje
&lt;/li&gt;
&lt;li&gt;Muestra su casa y escudo de Gryffindor
&lt;/li&gt;
&lt;li&gt;Muestra información sobre su hechizo
&lt;/li&gt;
&lt;li&gt;Muestra información de defensa
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si la respuesta hubiera sido un error, la app mostraría un mensaje de error.&lt;/p&gt;

&lt;p&gt;La app &lt;strong&gt;depende completamente&lt;/strong&gt; de lo que el servidor responda.&lt;/p&gt;

&lt;p&gt;Por eso cuando una API falla, la app no puede hacer nada, no puede inventar personajes, no puede adivinar información, solo puede mostrar que algo salió mal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué importa entender este flujo
&lt;/h2&gt;

&lt;p&gt;Porque cuando entiendes que una petición es un &lt;strong&gt;viaje&lt;/strong&gt;, no un evento instantáneo, empiezas a ver por qué:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Las apps a veces tardan en cargar (el viaje puede ser largo)
&lt;/li&gt;
&lt;li&gt;Los errores no siempre son culpa de la app (pueden pasar en cualquier punto del camino)
&lt;/li&gt;
&lt;li&gt;El backend tiene que ser confiable (es el que decide qué responder)
&lt;/li&gt;
&lt;li&gt;Las APIs bien diseñadas importan (hacen que todo este proceso sea predecible)
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No es magia.n Es un proceso con pasos claros.&lt;/p&gt;

&lt;p&gt;Y cuando algo falla, saber dónde buscar hace toda la diferencia.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que aprendiste
&lt;/h2&gt;

&lt;p&gt;Una petición no es un solo paso, es un viaje completo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;La app envía un mensaje
&lt;/li&gt;
&lt;li&gt;Ese mensaje viaja por la red
&lt;/li&gt;
&lt;li&gt;El servidor lo recibe y evalúa
&lt;/li&gt;
&lt;li&gt;El servidor decide qué responder
&lt;/li&gt;
&lt;li&gt;La respuesta regresa por el mismo camino
&lt;/li&gt;
&lt;li&gt;La app reacciona a lo que recibió
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Las aplicaciones &lt;strong&gt;dependen&lt;/strong&gt; de las respuestas.&lt;br&gt;&lt;br&gt;
Los servidores &lt;strong&gt;deciden&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Las APIs &lt;strong&gt;definen las reglas&lt;/strong&gt; de esa conversación.&lt;/p&gt;

&lt;p&gt;Entender este flujo te da claridad, y esa claridad es lo que te permite construir mejor, debuggear más rápido, y diseñar con más confianza.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué sigue
&lt;/h2&gt;

&lt;p&gt;En el siguiente artículo vamos a dar el siguiente paso: &lt;strong&gt;construiremos nuestra primera API REST&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Vas a ver cómo crear un servidor que reciba peticiones, procese información y devuelva respuestas.&lt;/p&gt;

&lt;p&gt;Ya entiendes el viaje completo de una petición.&lt;br&gt;&lt;br&gt;
Ahora es momento de construir el destino.&lt;/p&gt;

</description>
      <category>api</category>
      <category>español</category>
      <category>programming</category>
      <category>development</category>
    </item>
    <item>
      <title>AWS Valentine’s LATAM: cuando el amor también se despliega en la nube</title>
      <dc:creator>Hazel Saenz</dc:creator>
      <pubDate>Thu, 12 Feb 2026 01:22:37 +0000</pubDate>
      <link>https://dev.to/aws/aws-valentines-latam-cuando-el-amor-tambien-se-despliega-en-la-nube-28i9</link>
      <guid>https://dev.to/aws/aws-valentines-latam-cuando-el-amor-tambien-se-despliega-en-la-nube-28i9</guid>
      <description>&lt;p&gt;Una colección de frases dev-amor inspiradas en AWS, creadas desde LATAM para celebrar San Valentín con humor, ternura y comunidad.&lt;/p&gt;

&lt;p&gt;San Valentín no siempre se trata de flores o chocolates. A veces se trata de &lt;strong&gt;cómo explicamos lo que sentimos&lt;/strong&gt;, usando el lenguaje que mejor conocemos.&lt;/p&gt;

&lt;p&gt;En nuestro caso: &lt;strong&gt;el lenguaje de los devs&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  El origen de esta idea
&lt;/h2&gt;

&lt;p&gt;Hace unos días vi una serie de tarjetas de San Valentín creadas por &lt;strong&gt;&lt;a href="https://dev.to/aws/brookes-aws-valentines-day-cards-2026-5hhj"&gt;Brooke Jamieson&lt;/a&gt;&lt;/strong&gt; para la comunidad de AWS.  &lt;br&gt;
Eran divertidas, simples y muy nerd… y me hicieron sonreír.&lt;/p&gt;

&lt;p&gt;Pero sobre todo, me hicieron pensar:&lt;/p&gt;

&lt;p&gt;¿Cómo se vería esto si lo contáramos &lt;strong&gt;desde LATAM&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Con nuestro humor. Nuestra cercanía. Nuestra forma intensa, caótica y honesta de vivir tanto el código como las relaciones.&lt;/p&gt;

&lt;p&gt;Así nació &lt;strong&gt;AWS Valentine’s LATAM&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No como una traducción. Sino como una reinterpretación con identidad propia.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dev-amor, pero con acento latino
&lt;/h2&gt;

&lt;p&gt;Estas frases no están pensadas solo para ingenieras o ingenieros.  &lt;br&gt;
Están pensadas para cualquiera que haya:&lt;/p&gt;

&lt;p&gt;deployado con miedo, celebrado que &lt;em&gt;“ya funciona”&lt;/em&gt;, sufrido un outage o encontrado refugio en alguien que se queda cuando todo falla.&lt;/p&gt;

&lt;h3&gt;
  
  
  Eres mi región primaria 
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;(Amazon Route 53 / Multi-Region)&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Porque mi tráfico siempre apunta a ti.&lt;/em&gt;  &lt;strong&gt;Eres mi región primaria (y no hago failover).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyaoddifhc0mghuppzj42.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyaoddifhc0mghuppzj42.png" alt="route53" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Eres la respuesta correcta 
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;(Amazon API Gateway)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Eres la respuesta correcta,  a todas mis peticiones.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0n10a0ofami455u4wc9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0n10a0ofami455u4wc9.png" alt="ApiGateway" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  No importa la distancia 
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;(Amazon CloudFront)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;No importa la distancia,&lt;/em&gt;  &lt;strong&gt;igual llegas a mi edge.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzaq7n4yqfov38ahg2vds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzaq7n4yqfov38ahg2vds.png" alt="CloudFront" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Funciona por fe 
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;(AWS Lambda)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Funciona por fe,&lt;/em&gt;  &lt;strong&gt;y porque tú estás ahí.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u3bu6swrvgz4bgtsjf2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7u3bu6swrvgz4bgtsjf2.png" alt="lambda" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Nuestro amor sobrevivió 
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;(Amazon CloudWatch / Outage vibes)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Se cayó todo…&lt;/em&gt;  &lt;strong&gt;menos nosotros.&lt;/strong&gt;  Nuestro amor sobrevivió al outage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkwpkcseioe1uc3vh5chd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkwpkcseioe1uc3vh5chd.png" alt="Cloudwatch" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  No me asustas, me acompañas 
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;(Kiro – agentic AI, spec-driven dev)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;No me asustas, me acompañas.&lt;/em&gt;  Contigo todo empieza como idea  &lt;strong&gt;y termina en algo real.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvenbvjr79utjlcqu3idu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvenbvjr79utjlcqu3idu.png" alt="Kiro" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Guardado para siempre 
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;(Amazon S3)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Aunque el mundo se mueva,&lt;/em&gt;  &lt;strong&gt;lo nuestro no se pierde.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7kxidru51qjqac7lakbt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7kxidru51qjqac7lakbt.png" alt="S3" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ¿Por qué compartir esto?
&lt;/h2&gt;

&lt;p&gt;Porque la tecnología también puede ser &lt;strong&gt;suave&lt;/strong&gt;.  &lt;br&gt;
Porque no todo contenido técnico tiene que ser rígido.  &lt;br&gt;
Porque las comunidades se construyen con conocimiento, sí…  &lt;br&gt;
pero también con &lt;strong&gt;emoción, cercanía y humanidad&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Y porque en LATAM: &lt;strong&gt;amamos intenso, incluso cuando hablamos de la nube.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Para la comunidad
&lt;/h2&gt;

&lt;p&gt;Si alguna de estas frases te hizo sonreír, si te recordó a alguien, o si te dan ganas de compartirla en tu equipo, comunidad o meetup: misión cumplida.&lt;/p&gt;

&lt;p&gt;Gracias a la comunidad de AWS por inspirar, y a personas como &lt;strong&gt;Brooke&lt;/strong&gt; por recordarnos que el tech también puede ser divertido.&lt;/p&gt;

&lt;p&gt;Feliz San Valentín, desde LATAM, con amor y con cloud.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>watercooler</category>
      <category>español</category>
      <category>latam</category>
    </item>
    <item>
      <title>Qué pasa realmente cuando una app pide algo</title>
      <dc:creator>Hazel Saenz</dc:creator>
      <pubDate>Fri, 06 Feb 2026 01:40:19 +0000</pubDate>
      <link>https://dev.to/aws/que-pasa-realmente-cuando-una-app-pide-algo-19g0</link>
      <guid>https://dev.to/aws/que-pasa-realmente-cuando-una-app-pide-algo-19g0</guid>
      <description>&lt;h2&gt;
  
  
  &lt;em&gt;Una explicación simple para entender cómo se comunican las aplicaciones (y por qué a eso le llamamos API).&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Abres una app. ➡️ Tocas un botón. ➡️ Algo carga.&lt;br&gt;
No piensas mucho en eso. Solo esperas que funcione.&lt;br&gt;
Pero detrás de ese gesto tan simple, hay una conversación ocurriendo.&lt;/p&gt;

&lt;p&gt;Tu app de música no guarda millones de canciones en tu teléfono.&lt;br&gt;
Tu app del clima no tiene sensores en cada ciudad.&lt;br&gt;
Tu app de delivery no cocina la comida.&lt;/p&gt;

&lt;p&gt;Lo que hacen es pedirle esa información a otros sistemas.&lt;/p&gt;

&lt;p&gt;Esa conversación casi nunca se explica.&lt;/p&gt;

&lt;p&gt;Si alguna vez viste que una app “pide algo” y otra le responde, y sentiste que entendías lo suficiente para seguir, pero no tanto como para explicarlo… este artículo es para ti.&lt;/p&gt;
&lt;h2&gt;
  
  
  No es magia. Es una conversación.
&lt;/h2&gt;

&lt;p&gt;Cuando una aplicación necesita algo que no tiene —datos, resultados, información— no entra al sistema del otro lado como si fuera su casa.&lt;/p&gt;

&lt;p&gt;No toca lo que no le corresponde.&lt;br&gt;
No improvisa.&lt;br&gt;
No adivina.&lt;/p&gt;

&lt;p&gt;Hace algo más simple:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;pregunta&lt;/strong&gt;. Y espera una &lt;strong&gt;respuesta&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Imagina que abres Spotify y buscas "Bad Bunny".&lt;br&gt;
La app no tiene todas las canciones guardadas en tu teléfono.&lt;br&gt;
Hace una petición: "Dame las canciones de Bad Bunny".&lt;br&gt;
El servidor responde con la lista.&lt;/p&gt;

&lt;p&gt;La app hace una petición clara.&lt;br&gt;
El sistema del otro lado responde solo lo que prometió responder.&lt;/p&gt;

&lt;p&gt;Ni más. Ni menos. No hay magia. Hay reglas.&lt;/p&gt;
&lt;h2&gt;
  
  
  Antes de ponerle nombre, mira lo que pasa
&lt;/h2&gt;

&lt;p&gt;Una app necesita información.&lt;br&gt;
No sabe cómo está construido el sistema del otro lado.&lt;br&gt;
No le importa si usa Java, Python o cualquier otra cosa.&lt;/p&gt;

&lt;p&gt;Solo sabe dos cosas: qué puede pedir y qué va a recibir a cambio.&lt;/p&gt;

&lt;p&gt;Volviendo al ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Petición:
GET /buscar?artista=bad-bunny

Respuesta:
{
  "artista": "Bad Bunny",
  "canciones": ["DÁKITI", "Tití Me Preguntó", "Callaíta", ...]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La app no sabe dónde están guardadas esas canciones.&lt;br&gt;
No sabe si el servidor usa una base de datos SQL o NoSQL.&lt;br&gt;
Solo sabe que si pide de esa forma, va a recibir lo que necesita.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7kqplofiy2whpjo7ok4k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7kqplofiy2whpjo7ok4k.png" alt="comunications" width="800" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ese intercambio existe aunque casi nunca lo nombremos. Está ahí cada vez que una app pide algo y otra responde.&lt;/p&gt;

&lt;h2&gt;
  
  
  Eso tiene un nombre: API
&lt;/h2&gt;

&lt;p&gt;A esa forma de comunicarse se le llama &lt;strong&gt;API&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No es el sistema completo.&lt;br&gt;
No es la base de datos.&lt;br&gt;
No es la lógica interna.&lt;/p&gt;

&lt;p&gt;Es el &lt;strong&gt;acuerdo&lt;/strong&gt; que define cómo dos partes se hablan sin conocerse por dentro.&lt;br&gt;
Y aquí es donde empiezan muchos de los malentendidos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mito #1: “Una API es el backend”
&lt;/h2&gt;

&lt;p&gt;No. El backend es todo lo que pasa detrás:lógica, datos, procesos, decisiones.&lt;/p&gt;

&lt;p&gt;La API es &lt;strong&gt;la puerta&lt;/strong&gt;, no la casa.&lt;/p&gt;

&lt;p&gt;Es lo único que se muestra hacia afuera.&lt;br&gt;
Lo que marca el límite entre lo que se puede usar y lo que no.&lt;/p&gt;

&lt;p&gt;Cuando estas dos cosas se confunden, suele pasar que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;se exponen cosas que no deberían&lt;/li&gt;
&lt;li&gt;se rompen acuerdos sin darse cuenta&lt;/li&gt;
&lt;li&gt;se diseña pensando en el sistema, no en quien lo usa
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Mito #2: “Una API es solo una dirección”
&lt;/h2&gt;

&lt;p&gt;Muchas veces se piensa que una API es solo “el lugar al que haces la petición”.&lt;/p&gt;

&lt;p&gt;Como si todo se redujera a mandar algo y esperar respuesta.&lt;br&gt;
Pero una dirección por sí sola no explica nada.&lt;/p&gt;

&lt;p&gt;Puede decirte &lt;strong&gt;a dónde&lt;/strong&gt; ir, pero no te dice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;qué puedes pedir&lt;/li&gt;
&lt;li&gt;cómo hacerlo&lt;/li&gt;
&lt;li&gt;ni qué significa lo que recibes de vuelta
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Por ejemplo, &lt;code&gt;/buscar?artista=bad-bunny&lt;/code&gt; es una dirección.&lt;br&gt;
Pero sin saber que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;debes usar el método GET&lt;/li&gt;
&lt;li&gt;el parámetro se llama &lt;code&gt;artista&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;la respuesta será un JSON con canciones
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...esa dirección no te sirve de mucho.&lt;/p&gt;

&lt;p&gt;Esa dirección —a la que luego llamamos &lt;em&gt;endpoint&lt;/em&gt;—, el punto específico donde se hace la petición, solo cobra sentido cuando existen reglas claras alrededor.&lt;/p&gt;

&lt;p&gt;Una API no es solo el punto de entrada. Es el acuerdo completo que le da significado a la conversación.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entonces… ¿qué hacen realmente las APIs?
&lt;/h2&gt;

&lt;p&gt;Las APIs &lt;strong&gt;reducen el caos&lt;/strong&gt;. Permiten que sistemas distintos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;trabajen juntos
&lt;/li&gt;
&lt;li&gt;sin depender uno del otro
&lt;/li&gt;
&lt;li&gt;sin romperse cada vez que algo cambia
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Por ejemplo:&lt;/p&gt;

&lt;p&gt;Spotify puede mostrar canciones de Bad Bunny sin importar si:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;el servidor de música está en AWS o en otro cloud provider
&lt;/li&gt;
&lt;li&gt;la base de datos cambió de MySQL a PostgreSQL
&lt;/li&gt;
&lt;li&gt;el equipo de backend reescribió todo en otro lenguaje
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mientras la API siga respondiendo de la misma forma, la app sigue funcionando.&lt;/p&gt;

&lt;p&gt;Eso es lo que hace que puedas usar la misma app en tu teléfono durante años, aunque todo detrás haya cambiado varias veces.&lt;/p&gt;

&lt;p&gt;Cuando una API está bien diseñada, casi no se nota. Cuando no lo está, se siente en cada error, en cada confusión, en cada workaround.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué importa entender esto desde el inicio
&lt;/h2&gt;

&lt;p&gt;Porque cuando no tienes claro qué es una API, todo parece más complejo de lo que es.&lt;/p&gt;

&lt;p&gt;No porque falte aprender más, sino porque muchas veces nadie explicó bien el contexto.&lt;/p&gt;

&lt;h2&gt;
  
  
  Este es solo el comienzo
&lt;/h2&gt;

&lt;p&gt;Este artículo no es para memorizar definiciones.&lt;br&gt;
Es para empezar a &lt;strong&gt;ver&lt;/strong&gt; algo que ya estaba ocurriendo.&lt;/p&gt;

&lt;p&gt;En los siguientes artículos vamos a mirar con más detalle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;qué pasa realmente cuando una app hace una petición&lt;/li&gt;
&lt;li&gt;por qué REST no es el problema &lt;/li&gt;
&lt;li&gt;por qué muchas APIs fallan sin estar “rotas”&lt;/li&gt;
&lt;li&gt;y cómo diseñarlas pensando en personas, no solo en sistemas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si alguna vez sentiste que las APIs eran algo que usabas, pero que nadie te había explicado con claridad… A veces no falta aprender más, sino que alguien explique mejor.&lt;/p&gt;

</description>
      <category>api</category>
      <category>softwaredevelopment</category>
      <category>backend</category>
      <category>español</category>
    </item>
  </channel>
</rss>
