<script data-pm-proxy="intercept"></script><?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Clarity byDesign]]></title><description><![CDATA[Architecture decisions, tradeoffs, and the thinking behind building AI and data systems on AWS.]]></description><link>https://annpastushko.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!4deJ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F140abdfe-aae6-43fa-9de6-7951762d12df_553x553.png</url><title>Clarity byDesign</title><link>https://annpastushko.substack.com</link></image><generator>Substack</generator><lastBuildDate>Thu, 03 Sep 2026 16:55:59 GMT</lastBuildDate><atom:link href="/__u/annpastushko.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Anna Pastushko]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[annpastushko@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[annpastushko@substack.com]]></itunes:email><itunes:name><![CDATA[Anna Pastushko]]></itunes:name></itunes:owner><itunes:author><![CDATA[Anna Pastushko]]></itunes:author><googleplay:owner><![CDATA[annpastushko@substack.com]]></googleplay:owner><googleplay:email><![CDATA[annpastushko@substack.com]]></googleplay:email><googleplay:author><![CDATA[Anna Pastushko]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[🐾Recent AWS AI features that changed my architecture thinking🐾]]></title><description><![CDATA[&#129299; Last month, watching the AWS Summit New York keynote, I caught myself thinking &#8212; if half of these had existed a year ago, I would have designed some systems differently.]]></description><link>https://annpastushko.substack.com/p/recent-aws-ai-features-that-changed</link><guid isPermaLink="false">https://annpastushko.substack.com/p/recent-aws-ai-features-that-changed</guid><pubDate>Mon, 20 Jul 2026 13:03:13 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!gz6x!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F735ac86e-8c47-4e62-903e-1944972bc09f_2202x1337.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; Last month, watching the AWS Summit New York keynote, I caught myself thinking &#8212; if half of these had existed a year ago, I would have designed some systems differently. Not because my decisions were wrong, but because the constraints I designed around simply don&#8217;t exist anymore.</p><p>Here are the features that actually made me reconsider past decisions, and where I&#8217;d still think twice.</p><div><hr></div><h4>S3 Vectors &#8212; rethinking the vector store for the right use case</h4><p>Every RAG system I&#8217;ve built had the same component: a separate vector store. OpenSearch, Pinecone, pgvector &#8212; each with its own ops overhead and cost model.</p><p>S3 Vectors went generally available at re:Invent 2025, letting you store and query vector embeddings directly in S3, at significantly lower cost than dedicated vector databases. The architectural impact though is hitting projects now.</p><p>For projects where retrieval latency isn&#8217;t a hard constraint and the dataset isn&#8217;t updated in real time, this simplifies the architecture meaningfully. One less service to operate, and your embeddings live where your source documents already live.</p><blockquote><p>&#9888;&#65039; <strong>Reality check:</strong> If your use case requires frequent embedding updates or strict p99 latency SLAs, a dedicated vector store still wins. S3 Vectors makes most sense for stable datasets with mode rate query load &#8212; think document archives, not live feeds.</p></blockquote><h4>Bedrock Managed Knowledge Base &#8212; good for early stages, with caveats</h4><p>Data ingestion pipelines are often the unglamorous bottleneck in RAG projects: chunking strategies, format normalization, inconsistent document quality. For first project stages and MVPs, the new Bedrock Managed Knowledge Base can eliminate weeks of that work.</p><p>It provides native data connectors, Smart Parsing for automatic multi-format preparation, and an Agentic Retriever for multi-step queries.</p><p>The Agentic Retriever is architecturally interesting &#8212; it can decompose a complex question and retrieve across multiple steps rather than a single lookup. That&#8217;s a meaningfully different capability.</p><blockquote><p>&#9888;&#65039; <strong>Reality check:</strong> Managed means less control. If your data has unusual structure, domain-specific chunking requirements, or your retrieval strategy is where you differentiate &#8212; custom pipelines still make sense. I&#8217;d use this to move fast in early stages, then evaluate whether the limitations matter before scaling.</p></blockquote><h4>OpenSearch Serverless &#8212; autoscaling</h4><p>OpenSearch Serverless was rebuilt from the ground up recently, with instant autoscaling and up to 60% cost savings for dynamic workloads.</p><p>The original version had a known frustration: you paid for provisioned capacity even when traffic was low, which made it expensive for workloads that weren&#8217;t constant. The rebuild changes that calculus for search-heavy AI applications with unpredictable traffic patterns &#8212; think internal knowledge bases or tools used by specific teams at specific times.</p><blockquote><p>&#9888;&#65039; <strong>Reality check:</strong> &#8220;Up to 60% savings&#8221; depends heavily on your traffic pattern. For steady, predictable load, provisioned OpenSearch may still be cheaper. Worth running numbers on your actual usage profile before migrating.</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!gz6x!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F735ac86e-8c47-4e62-903e-1944972bc09f_2202x1337.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!gz6x!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F735ac86e-8c47-4e62-903e-1944972bc09f_2202x1337.png 424w, /__u/substackcdn.com/image/fetch/$s_!gz6x!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F735ac86e-8c47-4e62-903e-1944972bc09f_2202x1337.png 848w, /__u/substackcdn.com/image/fetch/$s_!gz6x!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F735ac86e-8c47-4e62-903e-1944972bc09f_2202x1337.png 1272w, /__u/substackcdn.com/image/fetch/$s_!gz6x!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F735ac86e-8c47-4e62-903e-1944972bc09f_2202x1337.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!gz6x!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F735ac86e-8c47-4e62-903e-1944972bc09f_2202x1337.png" width="1456" height="884" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/735ac86e-8c47-4e62-903e-1944972bc09f_2202x1337.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:884,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:571169,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/207401643?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F735ac86e-8c47-4e62-903e-1944972bc09f_2202x1337.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!gz6x!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F735ac86e-8c47-4e62-903e-1944972bc09f_2202x1337.png 424w, /__u/substackcdn.com/image/fetch/$s_!gz6x!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F735ac86e-8c47-4e62-903e-1944972bc09f_2202x1337.png 848w, /__u/substackcdn.com/image/fetch/$s_!gz6x!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F735ac86e-8c47-4e62-903e-1944972bc09f_2202x1337.png 1272w, /__u/substackcdn.com/image/fetch/$s_!gz6x!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F735ac86e-8c47-4e62-903e-1944972bc09f_2202x1337.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Have you tried S3 Vectors in any of your projects yet?<br>&#128172; Which of these would change your architecture the most?<br>&#128172; And what AWS features are you still waiting for that would actually make a difference?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/recent-aws-ai-features-that-changed/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/recent-aws-ai-features-that-changed/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[ 🐾Designing AI APIs like an architect: 4 main decisions🐾]]></title><description><![CDATA[&#129299; There is no shortage of material on AI API design &#8212; most of it focuses on implementation: how to set up streaming, how to configure async jobs, how to add rate limiting. What&#8217;s rarely covered is the architectural layer above that: which decisions to make, in what order, and how they constrain each other.

API design is one of the hardest things to change after launch. Your clients are already integrated, your contracts are set, and your state model is baked into production. This is not like refactoring a function.]]></description><link>https://annpastushko.substack.com/p/designing-ai-apis-like-an-architect</link><guid isPermaLink="false">https://annpastushko.substack.com/p/designing-ai-apis-like-an-architect</guid><pubDate>Mon, 06 Jul 2026 13:01:01 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!L_vT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb73abaa4-edc5-4dc0-a339-fe0cfe660155_1101x668.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; There is no shortage of material on AI API design &#8212; most of it focuses on implementation: how to set up streaming, how to configure async jobs, how to add rate limiting. What&#8217;s rarely covered is the architectural layer above that: which decisions to make, in what order, and how they constrain each other.</p><p>API design is one of the hardest things to change after launch. Your clients are already integrated, your contracts are set, and your state model is baked into production. This is not like refactoring a function.</p><p>These 4 decisions are the ones you need to make deliberately, before you write the first endpoint. Think of this as a pre-flight checklist &#8212; not a tutorial, but a map of what to consider and why the order matters.</p><div><hr></div><h4><strong>TL;DR</strong></h4><ul><li><p>Stateless API is the right default for anything that needs to scale &#8212; once you're stateless, workflow length determines whether you need streaming, async jobs, or both.</p></li><li><p>Streaming vs async jobs is a response delivery decision &#8212; stream when a user is watching, use async jobs when they&#8217;re not or when the workflow runs too long</p></li><li><p>Rate limiting for AI APIs means tokens, not just requests &#8212; RPM without TPM is incomplete</p></li><li><p>Pydantic (or Zod in TypeScript) serves three contracts: incoming requests, tool definitions, and structured model output</p></li></ul><div><hr></div><h4><strong>Why these decisions are not independent</strong></h4><p><strong>Stateless API </strong>is the foundational decision &#8212; it cascades into everything else. Once you commit to stateless design, your next question is workflow length: is a user watching and waiting, or is the work happening in the background? That single question determines whether <strong>streaming</strong> is enough or <strong>async</strong> jobs are required. From there, two more decisions apply regardless of which path you took &#8212; rate limiting and <strong>schema contracts</strong>. Get the foundation wrong and the rest won't save you.</p><h4><strong>1&#65039;&#8419; Stateless API</strong></h4><p>Not every AI API needs to be stateless. A local tool, a single-user assistant, or a quick prototype can hold conversation history in memory and work perfectly well. The trade-off becomes visible the moment you scale &#8212; multiple instances, concurrent users, pod restarts.</p><p>For any AI API that needs to scale beyond a single instance, stateless is the right default. Store conversation history in Redis or Postgres, pass a <em>thread_id</em> with every request, and let the backend load-call-save on each turn. The API itself becomes a stateless endpoint: any instance can handle any request, no sticky sessions, no lost conversations on pod restart, and full replay for debugging.</p><p>Once you&#8217;re stateless, the next question is workflow length &#8212; that determines whether streaming is enough or async jobs are required.</p><h4><strong>2&#65039;&#8419; Streaming vs async jobs</strong></h4><p>Once you&#8217;re stateless, you need to decide how the client receives results &#8212; and that depends entirely on whether a user is watching.</p><p>If they are, stream. Server-Sent Events keep the HTTP connection open and push each token as it&#8217;s generated. Perceived latency drops dramatically even if total generation time stays the same. SSE is the industry default for AI APIs &#8212; it&#8217;s one-directional, stateless over HTTP, and maps naturally onto how LLMs generate output. WebSockets are valid when you need bidirectional communication &#8212; for example, when a user can interrupt or redirect the agent mid-stream &#8212; but add handshake overhead and connection state you&#8217;ll need to manage.</p><p>If they&#8217;re not watching, or the workflow runs too long for a stable connection, return a <em>task_id</em> immediately and let the client poll or receive a webhook on completion. This lets you queue, retry, and prioritize work independently. A failed job replays without the user resubmitting.</p><p>Two downstream effects of streaming worth knowing: your rate limiting math changes (a cancelled stream still consumed input tokens), and your frontend now handles a stream, not a JSON object.</p><h4><strong>3&#65039;&#8419; Rate limiting: RPM + TPM</strong></h4><div class="pullquote"><p><strong>&#9888;&#65039; Regardless of which path you chose above &#8212; streaming, async jobs, or both &#8212; the following two decisions apply to every AI API.</strong></p></div><p>Standard rate limiting counts requests per minute. For AI APIs this is incomplete.</p><p>Ten requests per minute sounds reasonable. If each carries a 50,000-token conversation history, those 10 requests exhaust your upstream budget in seconds. Token-per-minute (TPM) limits are how you protect against this.</p><ul><li><p><strong>RPM</strong> (requests per minute) &#8212; protects against request floods and runaway clients</p></li><li><p><strong>TPM</strong> (tokens per minute) &#8212; protects against cost spikes from large-context requests and agentic loops that accumulate history</p></li></ul><p>The interaction with streaming: a cancelled stream still consumed your input tokens. Estimate token cost before calling the model &#8212; if the client exceeds their TPM budget, reject with 429 before inference starts. For most production APIs this is cheaper than paying for the inference first &#8212; though for low-traffic APIs the estimation overhead may not be worth it. </p><h4><strong>4&#65039;&#8419; Pydantic schemas</strong></h4><p>Pydantic does three jobs in an AI API. Most engineers know the first: validating incoming requests. In AI APIs it takes on two more &#8212; defining tool contracts for your agent and enforcing structured output from the model.</p><p>Two AI-specific places this matters. <strong>Tool definitions</strong> &#8212; when your agent calls tools, you describe each tool&#8217;s parameters as a schema. A loose schema produces unpredictable tool calls. A tight one with field descriptions and constraints produces reliable ones. <strong>Structured output</strong> &#8212; when you need the model to return JSON, define the expected shape as a Pydantic model. Most providers support constrained decoding, meaning the model can only generate tokens that produce valid JSON matching your schema. No more &#8220;parse the free-text response and hope for the best.&#8221;</p><p>Treat these schemas with the same care you&#8217;d give a public API contract. They are the interface definition between your deterministic code and your non-deterministic model. </p><blockquote><p>In TypeScript the same principle applies with Zod.</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!L_vT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb73abaa4-edc5-4dc0-a339-fe0cfe660155_1101x668.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!L_vT!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb73abaa4-edc5-4dc0-a339-fe0cfe660155_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!L_vT!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb73abaa4-edc5-4dc0-a339-fe0cfe660155_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!L_vT!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb73abaa4-edc5-4dc0-a339-fe0cfe660155_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!L_vT!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb73abaa4-edc5-4dc0-a339-fe0cfe660155_1101x668.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!L_vT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb73abaa4-edc5-4dc0-a339-fe0cfe660155_1101x668.png" width="1101" height="668" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b73abaa4-edc5-4dc0-a339-fe0cfe660155_1101x668.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:668,&quot;width&quot;:1101,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:153964,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/202617398?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb73abaa4-edc5-4dc0-a339-fe0cfe660155_1101x668.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!L_vT!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb73abaa4-edc5-4dc0-a339-fe0cfe660155_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!L_vT!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb73abaa4-edc5-4dc0-a339-fe0cfe660155_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!L_vT!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb73abaa4-edc5-4dc0-a339-fe0cfe660155_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!L_vT!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb73abaa4-edc5-4dc0-a339-fe0cfe660155_1101x668.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Which of these did you get wrong first time and had to redo?<br>&#128172; Are you running inference inside your web tier right now &#8212; how is it holding up?<br>&#128172; What would you add as a 7th decision?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/designing-ai-apis-like-an-architect/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/designing-ai-apis-like-an-architect/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾ReAct vs. State machines: Bounding the blast radius of autonomous agents🐾]]></title><description><![CDATA[In almost every production AI project, there is a distinct honeymoon phase. You build a prototype using a ReAct (Reasoning + Acting) loop, watch it dynamically pick tools to solve a complex query, and marvel at its autonomy. Then you deploy it. Within a week, a user inputs an edge-case prompt, the agent gets caught in an infinite loop alternating between two APIs, and your GenAI cost metrics spike overnight.

Moving from prototype to production forces a brutal architectural shift. You have to choose between raw LLM autonomy and rigid, deterministic control. But treating ReAct loops and State Machines as mutually exclusive binaries is a design trap.]]></description><link>https://annpastushko.substack.com/p/react-vs-state-machines-bounding</link><guid isPermaLink="false">https://annpastushko.substack.com/p/react-vs-state-machines-bounding</guid><pubDate>Mon, 22 Jun 2026 13:01:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!w2HW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6dccfd-ea2c-4a32-ab3d-e817c841ec20_1081x637.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; In almost every production AI project, there is a distinct honeymoon phase. You build a prototype using a ReAct (Reasoning + Acting) loop, watch it dynamically pick tools to solve a complex query, and marvel at its autonomy. Then you deploy it. Within a week, a user inputs an edge-case prompt, the agent gets caught in an infinite loop alternating between two APIs, and your GenAI cost metrics spike overnight.</p><p>Moving from prototype to production forces a brutal architectural shift. You have to choose between raw LLM autonomy and rigid, deterministic control. But treating ReAct loops and State Machines as mutually exclusive binaries is a design trap.</p><h4>TL;DR</h4><ul><li><p><strong>ReAct loops</strong> excel at <strong>dynamic discovery</strong>. Use them when the sequence of tool calls cannot be predicted at design time (e.g., open-ended research or diagnostics), and you can tolerate high latency and variable token costs.</p></li><li><p><strong>State machines</strong> excel at <strong>deterministic compliance</strong>. Use them when workflows require strict validation, predictable state transitions, and zero LLM autonomy over the execution path (e.g., transactional operations or financial checkouts).</p></li><li><p><strong>The Production meta-pattern:</strong> Never deploy an unconstrained ReAct loop. Instead, encapsulate the autonomous loop inside an fixed execution graph that acts as a <strong>programmatic escape hatch</strong> when the agent loses convergence.</p></li></ul><h4>What are they?</h4><p>When you build an agentic workflow, you are choosing <em>who owns the state router</em>: the model or your code.</p><p><strong>With ReAct</strong>, you hand control of <em>tool selection</em> to the model at runtime. This is the critical nuance most posts miss: the tools themselves are fully deterministic. A <code>query_database</code> tool always executes the same SQL. What&#8217;s autonomous is <em>which tool the model picks next</em>, and <em>when it decides to stop</em>. The workflow operates as an open-ended loop &#8212; Thought &#8594; Action &#8594; Observation &#8212; persisting until the model decides it has a final answer.</p><p>Here&#8217;s what &#8220;persisting&#8221; actually means in practice: every iteration appends to the context window. Thought, tool call, raw output, next thought &#8212; all of it accumulates. On a well-behaved three-step query, this is fine. On a diverging agent that keeps re-querying slightly different APIs hoping for a better result, your context window fills, your costs compound, and the model&#8217;s attention degrades. Context window accumulation isn&#8217;t a footnote &#8212; it&#8217;s the <em>primary</em> failure mode of unconstrained ReAct loops in production.</p><p>A lightweight mitigation that teams often overlook: enforce structured outputs (JSON schema) on each ReAct step. This forces the model to commit to a specific action format rather than free-text reasoning, which dramatically reduces both token waste and loop divergence. It doesn&#8217;t replace circuit breakers &#8212; it complements them.</p><p><strong>With State Machines</strong>, instead of giving the LLM full control, you structure the workflow as a directed graph of nodes (LangGraph, Step Functions, Prefect). Technically, a State Machine is a subset: it has a fixed set of states and strict transition rules. LangGraph is actually a DAG-based orchestration engine, which is more flexible. The distinction matters because DAGs allow conditional branching and parallel execution that a true FSM cannot. The LLM lives inside individual nodes &#8212; parsing input, generating a plan, classifying intent &#8212; but deterministic code controls which node fires next. Crucially, in plan-and-execute style, the model reasons once upfront &#8212; generating the full execution plan as structured output &#8212; then steps out. Deterministic code owns the rest.</p><p>One assumption that bites teams: rollbacks are <em>not</em> automatic. &#8220;Easy session rollbacks&#8221; only exist if you&#8217;ve explicitly implemented state checkpointing &#8212; LangGraph Persistence, Step Functions execution history, or your own snapshot mechanism. Design for this upfront, not as an afterthought when your billing pipeline fails mid-transaction.</p><h4>What to choose for your workflow?</h4><p>Choosing an architectural pattern for your production workloads isn't about finding the "smartest" agent; it's about managing predictability, state persistence, and your cloud budget. Both ReAct and State Machines are fully viable for production, but only if deployed within their specific operational boundaries.</p><p><strong>1&#65039;&#8419; The pure ReAct loop / Autonomous Agent</strong></p><p>Built for non-deterministic environments where the exact sequence of steps cannot be predicted at design time: automated log diagnostics, security threat hunting, open-ended research assistants.</p><ul><li><p><strong>Pros:</strong> Exceptional at zero-shot tool selection and open-ended discovery across complex, unpredictable reasoning paths.</p></li><li><p><strong>Cons:</strong> Compounding context window growth, unpredictable latency, non-deterministic paths. Without hard guardrails, susceptible to infinite loops that exhaust both your context budget and your API budget simultaneously.</p></li></ul><div class="callout-block" data-callout="true"><p>Use standalone <em><strong>only</strong></em> for non-blocking, exploratory background workloads where a user isn't waiting synchronously and execution time is variable. Even here, basic guardrails &#8212; max iterations, cost caps, loop detection &#8212; are table stakes, not optional.</p></div><p>2&#65039;&#8419; <strong>The pure State machine / Workflows</strong></p><p>Built for regulated, transactional systems where data integrity is non-negotiable: e-commerce checkout, banking compliance, CRM ingestion pipelines, multi-step form validation.</p><ul><li><p><strong>Pros:</strong> 100% deterministic routing, predictable token spending, native checkpointing (when configured), and clean rollback paths if a third-party API fails mid-transaction.</p></li><li><p><strong>Cons:</strong> Rigid by design. Unmapped edge cases hit dead ends. Requires significant upfront engineering to map every valid transition.</p></li></ul><div class="callout-block" data-callout="true"><p>Use for transactional enterprise operations. Mandatory for billing engines and strict data pipelines where execution safety and explicit validation schemas (Pydantic, JSON Schema) are hard requirements.</p></div><p>3&#65039;&#8419; <strong>Dual-Strategy Escapement / Guarded Agent</strong></p><p>This encapsulates a ReAct loop inside an Orchestration Graph that acts as a programmatic circuit breaker. The inner ReAct node handles loose, non-deterministic reasoning. The outer graph monitors hard metrics &#8212; iteration count, token consumption, semantic similarity between consecutive outputs (a strong signal the agent is spinning) &#8212; and forcibly intercepts execution if the agent fails to converge.</p><p>When the circuit trips, the system serializes current context and hands off to an immutable fallback route. The agent doesn&#8217;t just stop &#8212; it lands somewhere safe.</p><ul><li><p><strong>Pros:</strong> The best of both worlds. Dynamic tool selection with guaranteed infrastructure reliability. You define exactly what &#8220;out of bounds&#8221; looks like in code, not in a prompt.</p></li><li><p><strong>Cons:</strong> Two distinct execution layers to design and maintain. State serialization across paradigm shifts is non-trivial. You need telemetry to know when and why the escape hatch is firing.</p></li></ul><div class="callout-block" data-callout="true"><p>The gold standard for user-facing, multi-step GenAI applications.</p></div><p>4&#65039;&#8419; <strong>Upstream dynamic routing</strong></p><p>A lightweight classifier at the entry point evaluates the raw query once and routes it to the right execution engine &#8212; ReAct for exploratory intent, Orchestration Graph for structured transactions.</p><p>The key architectural decision here is <em>what kind of classifier</em>. An LLM classifier (Haiku-tier) adds roughly 80&#8211;150ms latency and a small token cost, but handles nuanced or ambiguous queries gracefully. A rule-based classifier &#8212; regex patterns, embedding similarity against intent clusters, keyword matching &#8212; adds near-zero latency and zero variable cost, but requires manual maintenance as query patterns evolve. In practice, most production systems combine both: rules handle the 80% of clear-cut cases, LLM handles the ambiguous tail.</p><p>Either way, this introduces a single point of failure. A misclassified unstructured query routed to a rigid graph hits a dead end. Invest in classifier monitoring before you deploy this pattern at scale.</p><ul><li><p><strong>Pros:</strong> Massive cost and performance optimization. Simple queries completely bypass the token-heavy ReAct loop.</p></li><li><p><strong>Cons:</strong> Permanent latency floor at the routing layer (rule-based: ~5ms, LLM-based: ~80&#8211;150ms). Misclassification sends the payload to the wrong engine.</p></li></ul><div class="callout-block" data-callout="true"><p>The ideal gateway pattern for unified chat interfaces that cover both exploratory analysis and structured backend operations.</p></div><h4><strong>&#9888;&#65039; The circuit breaker</strong></h4><p>Every pattern above assumes your autonomous loop can eventually resolve itself &#8212; programmatically. That assumption breaks in high-stakes domains.</p><p>Human-in-the-loop is not a UX feature. It&#8217;s an architectural circuit breaker. When an agent has consumed N iterations without convergence, or when it&#8217;s about to execute an irreversible action (send an email, place an order, delete a record), the correct escape hatch is often a human approval step &#8212; not a fallback route.</p><p>Step Functions natively supports this with <code>.waitForTaskToken</code>. LangGraph Persistence lets you pause a graph and resume it after external input. The pattern is well-understood. What&#8217;s less understood is <em>where</em> to place the interrupt: the answer is at any node that crosses an irreversibility boundary, not just at max-iteration limits.</p><p>If you&#8217;re building anything that touches real-world side effects, design the HITL interrupt points <em>before</em> you design the agent&#8217;s tool set.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!w2HW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6dccfd-ea2c-4a32-ab3d-e817c841ec20_1081x637.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!w2HW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6dccfd-ea2c-4a32-ab3d-e817c841ec20_1081x637.png 424w, /__u/substackcdn.com/image/fetch/$s_!w2HW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6dccfd-ea2c-4a32-ab3d-e817c841ec20_1081x637.png 848w, /__u/substackcdn.com/image/fetch/$s_!w2HW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6dccfd-ea2c-4a32-ab3d-e817c841ec20_1081x637.png 1272w, /__u/substackcdn.com/image/fetch/$s_!w2HW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6dccfd-ea2c-4a32-ab3d-e817c841ec20_1081x637.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!w2HW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6dccfd-ea2c-4a32-ab3d-e817c841ec20_1081x637.png" width="1081" height="637" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3b6dccfd-ea2c-4a32-ab3d-e817c841ec20_1081x637.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:637,&quot;width&quot;:1081,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:248594,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/202329097?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6dccfd-ea2c-4a32-ab3d-e817c841ec20_1081x637.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!w2HW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6dccfd-ea2c-4a32-ab3d-e817c841ec20_1081x637.png 424w, /__u/substackcdn.com/image/fetch/$s_!w2HW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6dccfd-ea2c-4a32-ab3d-e817c841ec20_1081x637.png 848w, /__u/substackcdn.com/image/fetch/$s_!w2HW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6dccfd-ea2c-4a32-ab3d-e817c841ec20_1081x637.png 1272w, /__u/substackcdn.com/image/fetch/$s_!w2HW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6dccfd-ea2c-4a32-ab3d-e817c841ec20_1081x637.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Where do you draw the line between "let the agent retry" and "escalate to a human"?<br>&#128172; Have you implemented structured outputs on ReAct steps? Did it reduce loop divergence?<br>&#128172; What's your circuit breaker metric &#8212; iteration count, token budget, or something else?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/react-vs-state-machines-bounding/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/react-vs-state-machines-bounding/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾Should you use SQS or EventBridge?🐾]]></title><description><![CDATA[&#129299; In almost every AWS project, you&#8217;ll face a moment where services need to communicate asynchronously. That&#8217;s when teams often start comparing SQS and EventBridge. The catch? They&#8217;re not really competitors. They solve different problems, and many production architectures use both together. Understanding where each service fits can save a lot of complexity later.]]></description><link>https://annpastushko.substack.com/p/should-you-use-sqs-or-eventbridge</link><guid isPermaLink="false">https://annpastushko.substack.com/p/should-you-use-sqs-or-eventbridge</guid><pubDate>Mon, 08 Jun 2026 13:01:31 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hL3W!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4454244-ec9a-45c5-9b67-69af1b2288b8_1220x1448.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; In almost every AWS project, you&#8217;ll face a moment where services need to communicate asynchronously. That&#8217;s when teams often start comparing SQS and EventBridge. The catch? They&#8217;re not really competitors. They solve different problems, and many production architectures use both together. Understanding where each service fits can save a lot of complexity later.</p><div><hr></div><h4>TL;DR</h4><p><strong>A useful mental model: </strong>queues primarily solve workload buffering and backpressure, while event buses primarily solve service decoupling and event distribution.</p><p>Use SQS when:</p><ul><li><p>You need <strong>reliable, point-to-point processing</strong> where consumers explicitly pull messages at their own pace (pull-based buffering).</p></li><li><p><strong>Strict worker isolation</strong> matters &#8212; you want dedicated queues so one failing consumer never stalls or risks impacting others.</p></li><li><p>You are dealing with highly uniform workloads or heavy processing pipelines that require precise chunking/batching strategies.</p></li></ul><p>Use EventBridge when:</p><ul><li><p><strong>Multiple independent services</strong> need to react to the same architectural event.</p></li><li><p>You want <strong>loose coupling</strong> &#8212; the producing system fires an event globally and doesn&#8217;t know or care who listens.</p></li><li><p>You need <strong>complex routing and filtering rules</strong> evaluated directly on the infrastructure layer before hits reach your code.</p></li><li><p>You&#8217;re building native <strong>cross-account workflows or SaaS integrations</strong> (e.g., Zendesk, Stripe, PagerDuty).</p></li><li><p>Advanced features like a schema registry, native event archiving, or automated event replay are required.</p></li></ul><h4>What are they?</h4><p><strong>SQS</strong> is a message queue. Producers place messages into a queue, and consumer workers pull them for processing. While multiple consumers can process messages from the same queue, each individual message is delivered to and processed by only one consumer at a time. Messages persist safely in the queue until the consumer deletes them. The consumer is in control.</p><div class="callout-block" data-callout="true"><p>&#128161; <em>Myth 1: &#8220;Stop using SNS + SQS for fan-out; EventBridge replaces it with fewer steps.&#8221;</em> <br><strong>Fact:</strong> The classic <strong>SNS + SQS fan-out pattern</strong> remains an AWS Well-Architected standard for high-throughput systems. SNS scales to extremely high throughput and is generally more cost-effective than EventBridge for large-scale fan-out workloads.</p></div><p><strong>EventBridge</strong> is an event bus. One event fires, and a central router pushes copies of it to multiple matched targets simultaneously. The system relies on a publish/subscribe, push-based delivery model.</p><div class="callout-block" data-callout="true"><p>&#128161; <em>Myth 2: &#8220;EventBridge doesn&#8217;t support message ordering or Dead Letter Queues.&#8221;</em> <br><strong>Fact:</strong> AWS architecture moves fast. EventBridge natively supports <strong>FIFO event buses</strong> for strict sequence ordering (introduced in 2023) and permits you to attach dedicated <strong>Dead-Letter Queues (DLQs)</strong> directly to event targets to cleanly handle delivery drop-offs.</p></div><p>The wrong structural choice won&#8217;t break your system immediately. It just changes how hard your application is to maintain, scale, and reason about over time.</p><div id="datawrapper-iframe" class="datawrapper-wrap outer" data-attrs="{&quot;url&quot;:&quot;https://datawrapper.dwcdn.net/FoRqt/2/&quot;,&quot;thumbnail_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d4454244-ec9a-45c5-9b67-69af1b2288b8_1220x1448.png&quot;,&quot;thumbnail_url_full&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/96d553a5-5a5a-4e2c-9ccd-ebe7b238590a_1220x1518.png&quot;,&quot;height&quot;:776,&quot;title&quot;:&quot;Use cases breakdown&quot;,&quot;description&quot;:&quot;&quot;,&quot;belowTheFold&quot;:true}" data-component-name="DatawrapperToDOM"><iframe id="iframe-datawrapper" class="datawrapper-iframe" src="https://datawrapper.dwcdn.net/FoRqt/2/" width="730" height="776" frameborder="0" scrolling="no" loading="lazy"></iframe><script type="text/javascript">!function(){"use strict";window.addEventListener("message",(function(e){if(void 0!==e.data["datawrapper-height"]){var t=document.querySelectorAll("iframe");for(var a in e.data["datawrapper-height"])for(var r=0;r<t.length;r++){if(t[r].contentWindow===e.source)t[r].style.height=e.data["datawrapper-height"][a]+"px"}}}))}();</script></div><blockquote><p>&#9888;&#65039; FIFO Event Buses supported (introduced in 2023); suitable for ordering guarantees, though FIFO SQS remains the more common choice for ordered processing workloads</p></blockquote><h4>Why choose when you can use both?</h4><p>A common misconception is that architects must choose either SQS or EventBridge. In practice, many production AWS systems use both together. EventBridge handles event distribution, filtering, cross-account routing, and SaaS integrations, while SQS provides consumer isolation, buffering, retries, and backpressure protection. A common pattern is Producer &#8594; EventBridge &#8594; SQS &#8594; Consumer, allowing teams to benefit from both loose coupling and resilient workload processing.</p><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Which do you reach for first &#8212; SQS or EventBridge?<br>&#128172; Have you ever had to migrate from one to the other?<br>&#128172; What's your most common pattern?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/should-you-use-sqs-or-eventbridge/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/should-you-use-sqs-or-eventbridge/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾The hidden cost model of RAG in Amazon OpenSearch🐾]]></title><description><![CDATA[&#129299; Most teams building RAG pipelines obsess over LLM costs &#8212; token counts, model tiers, prompt lengths. And then the OpenSearch bill arrives. Vector search at scale has its own cost model, and it's rarely visible until it's already compounding. The problem isn't retrieval itself. It's retrieval designed without economic intent.]]></description><link>https://annpastushko.substack.com/p/the-hidden-cost-model-of-rag-in-amazon</link><guid isPermaLink="false">https://annpastushko.substack.com/p/the-hidden-cost-model-of-rag-in-amazon</guid><pubDate>Mon, 25 May 2026 13:02:49 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!fHln!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93d008b2-9af1-4417-a8b8-907900b2e8e0_1101x668.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; Most teams building RAG pipelines obsess over LLM costs &#8212; token counts, model tiers, prompt lengths. And then the OpenSearch bill arrives. Vector search at scale has its own cost model, and it's rarely visible until it's already compounding. The problem isn't retrieval itself. It's retrieval designed without economic intent.</p><h4>&#9888;&#65039; The Trap: Relevance-only thinking</h4><p>When designing RAG systems, the natural instinct is to optimize for answer quality. Better chunking, better embeddings, better retrieval &#8212; all measured by how relevant the results are. Cost enters the conversation later, usually when it&#8217;s already compounding.</p><p>Every decision in your retrieval layer is also a cost decision:</p><ul><li><p><strong>How you chunk documents</strong> determines how many vectors you store and search across at runtime</p></li><li><p><strong>The embedding model and size you choose</strong> determines your index footprint</p></li><li><p><strong>How you design your metadata fields</strong> determines whether query-time filtering narrows the search space effectively or adds overhead without reducing it</p></li></ul><blockquote><p>None of these feels like financial choices when you&#8217;re making them. They all are.</p></blockquote><h4>&#9888;&#65039; The Trap: Treating all documents as equally Hot</h4><p>OpenSearch offers storage tiers, and most RAG implementations ignore them entirely. This is an architectural oversight, not an engineering one.</p><p>The decision worth making explicitly: <strong>not all knowledge is equally hot</strong>. Documents ingested last week and queried daily have different economics than documents from two years ago queried occasionally. Treating them identically &#8212; keeping everything on high-performance storage &#8212; is a choice, and it&#8217;s usually the wrong one.</p><p>One important constraint: ANN indexes often can't be queried efficiently from cold storage without latency or rehydration cost. The lifecycle model applies to content that can tolerate slower retrieval &#8212; archival context, rarely accessed knowledge bases. For latency-sensitive RAG, "cold" may mean a separate index with a different serving strategy, not just a cheaper tier.</p><blockquote><p>The architect&#8217;s job is to define the access pattern taxonomy upfront: what&#8217;s hot, what&#8217;s warm, what&#8217;s cold &#8212; and design an index lifecycle policy that matches the retrieval SLA to the storage cost. This is a business decision dressed as a technical one. </p></blockquote><h4>&#9888;&#65039; The Trap: Searching blindly</h4><p>How retrieval queries are structured has a bigger cost impact than most architects realize. The core tradeoff is simple: <strong>the narrower the search space before similarity ranking, the cheaper each query is</strong>.</p><p>If your RAG system serves multiple use cases, tenant segments, or vast document categories, scanning the entire global index for every query means you traverse the full vector graph regardless of relevance. You pay the full compute cost of a wide search, only to discard most results afterward. The alternative is to pre-filter for the subset of data that matters. At low volume this is invisible. At scale, it&#8217;s a budget killer. </p><p>The goal is to design a data model that enforces search space reduction upfront. </p><blockquote><p>The architect-level question isn&#8217;t &#8220;how do we filter?&#8221; &#8212; it&#8217;s how do we model our data so filtering is always possible and always cheap? Are we relying on native metadata pre-filtering within a single index, or do we need strict physical partitioning across separate indices and shards?</p></blockquote><h4>&#9888;&#65039; The Trap: Assuming Vector Search Costs Scale Linearly</h4><p>Traditional database costs are predictable &#8212; more rows, more storage, roughly linear. Vector search doesn&#8217;t behave that way. The index that makes approximate nearest neighbor search fast is memory-resident and grows non-linearly with your corpus. This is the architectural surprise most teams hit too late.</p><p>The right mental model: <strong>you&#8217;re not paying per query, you&#8217;re paying for the index you&#8217;ve designed</strong>. A corpus of 10M small chunks is fundamentally more expensive to serve than 2M well-designed chunks with equivalent recall &#8212; not just in storage, but in the compute needed to search it. Well-designed here means: chunks sized so that recall doesn't require retrieving excessive top-k to compensate for poor granularity. Chunking is a multi-objective decision &#8212; too small inflates index size, too large degrades recall and forces broader retrieval to compensate.</p><blockquote><p>Architect&#8217;s question: At 10x your current document volume, does your retrieval cost grow linearly or does it compound? If you don&#8217;t know, your index design hasn&#8217;t been stress-tested economically.</p></blockquote><h4>&#9989; The Mindset shift: Retrieval as an economic system</h4><p>The pattern I see repeatedly: teams make retrieval decisions to hit a relevance benchmark, then treat cost as something to address later. By the time cost becomes visible, the index design is load-bearing &#8212; hard to change without rebuilding the pipeline.</p><p>The better approach is to treat retrieval design as an optimization across two axes simultaneously: <strong>recall quality and cost per query at scale</strong>. These aren&#8217;t in conflict if you design for both from the start. They become very much in conflict if you don&#8217;t.</p><p>Four questions worth answering before you finalize any RAG architecture:</p><ol><li><p>What is our acceptable recall threshold &#8212; and have we tested whether a leaner design meets it?</p></li><li><p>What does our query volume look like at 10x, and what does that do to our index cost?</p></li><li><p>Do we have a document lifecycle model, or does everything live on hot storage forever?</p></li><li><p>Can our data model support pre-filtering, or are we searching blind every time?</p></li></ol><p>Retrieval is not infrastructure you configure and forget. It&#8217;s a system with its own cost surface &#8212; and the architect&#8217;s job is to make that surface visible before the bill does.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!fHln!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93d008b2-9af1-4417-a8b8-907900b2e8e0_1101x668.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!fHln!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93d008b2-9af1-4417-a8b8-907900b2e8e0_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!fHln!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93d008b2-9af1-4417-a8b8-907900b2e8e0_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!fHln!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93d008b2-9af1-4417-a8b8-907900b2e8e0_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!fHln!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93d008b2-9af1-4417-a8b8-907900b2e8e0_1101x668.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!fHln!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93d008b2-9af1-4417-a8b8-907900b2e8e0_1101x668.png" width="1101" height="668" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/93d008b2-9af1-4417-a8b8-907900b2e8e0_1101x668.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:668,&quot;width&quot;:1101,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:116637,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/198669811?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93d008b2-9af1-4417-a8b8-907900b2e8e0_1101x668.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!fHln!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93d008b2-9af1-4417-a8b8-907900b2e8e0_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!fHln!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93d008b2-9af1-4417-a8b8-907900b2e8e0_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!fHln!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93d008b2-9af1-4417-a8b8-907900b2e8e0_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!fHln!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F93d008b2-9af1-4417-a8b8-907900b2e8e0_1101x668.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; What's your biggest OpenSearch cost surprise so far?<br>&#128172; Did you design your RAG retrieval layer with cost in mind from the start &#8212; or did it come later?<br>&#128172; How do you balance recall quality vs. retrieval cost in your architecture reviews?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/the-hidden-cost-model-of-rag-in-amazon/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/the-hidden-cost-model-of-rag-in-amazon/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾LLMs are not Just Another Microservice🐾]]></title><description><![CDATA[&#129299; When engineers first integrate an LLM into a production system, the initial implementation feels familiar. You construct an HTTP request, attach a prompt template, parse the response, and wire it into your existing service. The API surface looks like any other external dependency you&#8217;ve worked with, so you apply the same patterns you&#8217;d use for any reliable service: synchronous calls, standard retry logic, typed response handling.

That familiarity is the trap. LLMs carry a fundamentally different operational contract. The assumptions baked into your infrastructure &#8212; about latency bounds, output determinism, and failure modes &#8212; don&#8217;t hold. Not because the API is poorly designed, but because the underlying system behaves differently at a fundamental level.]]></description><link>https://annpastushko.substack.com/p/llms-are-not-just-another-microservice</link><guid isPermaLink="false">https://annpastushko.substack.com/p/llms-are-not-just-another-microservice</guid><pubDate>Mon, 11 May 2026 13:03:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!z52p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bc3818f-96db-41ce-b154-47f87975ce87_1101x668.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; When engineers first integrate an LLM into a production system, the initial implementation feels familiar. You construct an HTTP request, attach a prompt template, parse the response, and wire it into your existing service. The API surface looks like any other external dependency you&#8217;ve worked with, so you apply the same patterns you&#8217;d use for any reliable service: synchronous calls, standard retry logic, typed response handling.</p><p><strong>That familiarity is the trap. </strong>LLMs carry a fundamentally different operational contract. The assumptions baked into your infrastructure &#8212; about latency bounds, output determinism, and failure modes &#8212; don&#8217;t hold. Not because the API is poorly designed, but because the underlying system behaves differently at a fundamental level. </p><div><hr></div><h4><strong>Latency is not a number, it&#8217;s a distribution</strong></h4><p>Most internal services operate within predictable bounds &#8212; tens or low hundreds of milliseconds. LLM inference doesn&#8217;t work this way. Response time is a function of output token count, model size, and provider load at the moment of the request. A call that completes in 2 seconds normally can stretch to 15 or more under pressure &#8212; with no signal in advance telling you which scenario you&#8217;re in. Call LLMs synchronously from request threads and you&#8217;re one traffic spike away from a thread pool entirely blocked on AI inference, while unrelated parts of your system time out waiting for workers. This is upstream resource exhaustion &#8212; a cascading failure pattern where one slow dependency poisons the entire request pool.</p><p>&#9989; <strong>What to do:</strong> Decouple submission from completion. Async job queues let you accept the request immediately and process in the background. Where you can&#8217;t go async, use streaming &#8212; it doesn&#8217;t reduce total latency, but delivers the first token fast and keeps the user engaged. Note the distinction between TTFT (Time to First Token) and total latency &#8212; in production you often optimize them independently, since TTFT drives perceived responsiveness even when total generation time remains high.</p><h4><strong>The output is a promise, not a contract</strong></h4><p>A typed API returns a known shape. An LLM returns text that looks like what you asked for &#8212; until it doesn&#8217;t. The failure modes are subtle: a field is missing, a key gets renamed, the model wraps JSON in extra prose, a value changes shape. These aren&#8217;t edge cases &#8212; they&#8217;re the expected behavior of a probabilistic system at scale. The gap between &#8220;the LLM gave me a JSON string&#8221; and &#8220;this data is ready for my schema&#8221; is where production bugs hide &#8212; the kind that pass every test written during the PoC.</p><blockquote><p>&#9989; <strong>What to do:</strong> Treat the LLM as an untrusted component at your system boundary. Validate every response against a schema before it touches your business logic. Use constrained decoding where available &#8212; provider-native JSON modes or libraries like Outlines and Guidance. They enforce schema at the token generation level, not after the fact. The model physically cannot produce output that violates the schema. A degraded fallback beats silent data corruption.</p></blockquote><h4><strong>Naive retries will hurt you</strong></h4><p>Exponential backoff works reliably for transient failures in databases and downstream HTTP calls. With LLMs, the same logic applied carelessly becomes expensive and misleading. Retrying isn&#8217;t free &#8212; it costs tokens, time, and money. A 2,000-token request retried three times has consumed 8,000 tokens on a single user action, with output tokens costing 3-5x more than input. Worse, blind retries mask the real problem: if output consistently fails validation, the issue is in your prompt or schema design. Retrying won&#8217;t fix it, it&#8217;ll just obscure the signal. If a model is returning malformed output because your prompt is ambiguous, sending the same prompt again will produce the same malformed output &#8212; just slower and more expensively.</p><blockquote><p>&#9989; <strong>What to do:</strong> Classify failures before retrying. A 429 rate limit warrants backoff with jitter. A 500 means the provider is down &#8212; back off and consider a fallback model. A content filter violation means retrying the same prompt is guaranteed to fail and waste money. Each error class needs its own handling path. Set token budgets per request path and alert when they&#8217;re regularly exceeded.</p></blockquote><h4><strong>The model under you can change without warning</strong></h4><p>A traditional dependency ships breaking changes with a version bump and a migration guide. LLM providers don&#8217;t always extend the same courtesy. A prompt that works perfectly today can quietly degrade after a model update &#8212; your error rate stays flat, your latency looks normal, but the model stopped following an instruction it previously respected. Downstream, a dashboard shows wrong numbers that nobody notices for a week. The problem compounds when you&#8217;ve tuned prompts against a specific model version and have no record of what changed or when.</p><blockquote><p>&#9989; <strong>What to do:</strong> Version your prompts and schemas, and log both alongside every model response. When validation error rates rise, you need to correlate against what was active at the time. Pin to specific model versions where your provider allows it, and treat behavior regression as a first-class incident &#8212; the same way you&#8217;d treat a latency spike or an elevated error rate.</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!z52p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bc3818f-96db-41ce-b154-47f87975ce87_1101x668.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!z52p!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bc3818f-96db-41ce-b154-47f87975ce87_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!z52p!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bc3818f-96db-41ce-b154-47f87975ce87_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!z52p!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bc3818f-96db-41ce-b154-47f87975ce87_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!z52p!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bc3818f-96db-41ce-b154-47f87975ce87_1101x668.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!z52p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bc3818f-96db-41ce-b154-47f87975ce87_1101x668.png" width="1101" height="668" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6bc3818f-96db-41ce-b154-47f87975ce87_1101x668.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:668,&quot;width&quot;:1101,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:246923,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/191136742?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bc3818f-96db-41ce-b154-47f87975ce87_1101x668.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!z52p!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bc3818f-96db-41ce-b154-47f87975ce87_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!z52p!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bc3818f-96db-41ce-b154-47f87975ce87_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!z52p!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bc3818f-96db-41ce-b154-47f87975ce87_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!z52p!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6bc3818f-96db-41ce-b154-47f87975ce87_1101x668.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Have you been burned by any of these assumptions in production?<br>&#128172; What was the first thing that broke?<br>&#128172; What&#8217;s the weirdest &#8220;production-only&#8221; LLM failure you&#8217;ve seen?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/llms-are-not-just-another-microservice/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/llms-are-not-just-another-microservice/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾How one project shaped my architecture process🐾]]></title><description><![CDATA[&#129299; I want to tell you about a project that looked straightforward at every stage. The request was clear. The design made sense. The team was experienced. And yet by the time we shipped, the system had drifted so far from the original design that the stakeholder barely recognized it.
Here&#8217;s what happened at each stage &#8212; and what I&#8217;d do differently.]]></description><link>https://annpastushko.substack.com/p/how-you-build-the-new-workload</link><guid isPermaLink="false">https://annpastushko.substack.com/p/how-you-build-the-new-workload</guid><pubDate>Mon, 27 Apr 2026 13:03:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!5TZk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F480f1a53-261e-47d5-b824-449a3e446d17_1101x668.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; I want to tell you about a project that looked straightforward at every stage. The request was clear. The design made sense. The team was experienced. And yet by the time we shipped, the system had drifted so far from the original design that the stakeholder barely recognized it.</p><p>Here&#8217;s what happened at each stage &#8212; and what I&#8217;d do differently. </p><div><hr></div><h4>Phase 1: What are we actually building?</h4><p>The request sounded simple: <em>ingest documents, make them searchable via chat.</em> So it&#8217;s tempting to jump straight into service selection. I knew the stack. Why waste time on discovery sessions? </p><p>Because &#8220;documents&#8221; means different things to different people, one of the important things I learnt from my mentor. </p><p>So I scheduled a discovery session and started asking questions:</p><ol><li><p><strong>What documents?</strong> The documents include HR policies, legal contracts, and employee performance reviews. That&#8217;s PII. That&#8217;s compliance. Nobody mentioned it in the initial brief because to the stakeholder it was obvious &#8212; of course, HR docs are sensitive. For me, it wasn&#8217;t, because I hadn&#8217;t asked yet. </p></li><li><p><strong>What formats?</strong> PDFs, mostly. Some are scanned. Some are 200 pages. </p></li><li><p><strong>What does &#8220;real-time&#8221; mean?</strong> Turns out they mean answers within 30 seconds, not sub-second latency. That&#8217;s a very different architecture constraint. </p></li><li><p><strong>Who uploads documents?</strong> Not a developer &#8212; it&#8217;s an HR coordinator who has never used AWS in their life.</p></li></ol><div class="callout-block" data-callout="true"><p>None of this was in the original request. All of this changes your design.</p></div><h4>Phase 2: Now you design. Not guess</h4><p>I sat down with my Discovery outputs. It felt like I finally had all the answers &#8212; the design should be straightforward now.</p><p>Some decisions were obvious once I knew the context:</p><ul><li><p>200-page documents meant synchronous Lambda processing wouldn&#8217;t work &#8212; I needed an async pipeline with Step Functions to handle long-running jobs without hitting the 15-minute timeout wall</p></li><li><p>PII meant access controls, audit logging, and a way to scan and redact sensitive content before it gets indexed</p></li></ul><p>But some choices were not obvious at all. Take the embedding model &#8212; I had two options, and neither was clearly right:</p><ol><li><p><strong>Managed embedding service (Bedrock)</strong> &#8212; simple, no infrastructure overhead, fast to set up. But data leaves your VPC. I imagined presenting this to the stakeholder six months in, when the compliance team asks why employee performance reviews are being sent to an external API. Nobody would have flagged it because nobody had the conversation.</p></li><li><p><strong>Self-hosted model on ECS </strong>&#8212; stays in your network, full control. But the team needs to maintain it. And when the model gets updated, every chunk in the vector DB becomes stale and needs reprocessing. If that&#8217;s not explicitly planned in the design, it will surprise someone in production.</p></li></ol><p>Neither option was wrong. What would have been wrong is choosing one without showing the stakeholder both, and what each one actually costs them.</p><div class="callout-block" data-callout="true"><p>Some decisions follow directly from what you learned in Discovery. Others require a conversation you haven't had yet. Your job is to know the difference.</p></div><h4>Phase 3: Someone has to actually build this</h4><p>Design was approved. I felt good about the architecture. Then I handed it over to the engineering team.</p><p>Day one, they had questions:</p><ul><li><p>Why Textract and not a custom parser &#8212; it&#8217;s cheaper?</p></li><li><p>Why Step Functions and not just a longer Lambda timeout?</p></li></ul><p>These were reasonable questions. But I realized I had made a mistake &#8212; I had been so focused on getting the design right that I hadn&#8217;t brought the team along. They were hearing the reasoning for the first time while they were already supposed to be building. Some decisions got quietly reconsidered. Some got implemented differently because nobody had explained the <em>why</em> to the people actually writing the code.</p><p>The system that got shipped had started to drift from the system I had designed.</p><div class="callout-block" data-callout="true"><p>The team building the system will make decisions too. Whether they make them with context or without it depends on how early you involve them.</p></div><div><hr></div><p>These three lessons came from one project. But the same patterns kept showing up across others &#8212; different stacks, different stakeholders, same gaps. Over time, I started writing down what I was learning. </p><p>I called it <a href="https://d3-architecture-framework.vercel.app">D3 for Discover &#8594; Design &#8594; Deploy</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!5TZk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F480f1a53-261e-47d5-b824-449a3e446d17_1101x668.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!5TZk!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F480f1a53-261e-47d5-b824-449a3e446d17_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!5TZk!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F480f1a53-261e-47d5-b824-449a3e446d17_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!5TZk!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F480f1a53-261e-47d5-b824-449a3e446d17_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!5TZk!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F480f1a53-261e-47d5-b824-449a3e446d17_1101x668.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!5TZk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F480f1a53-261e-47d5-b824-449a3e446d17_1101x668.png" width="1101" height="668" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/480f1a53-261e-47d5-b824-449a3e446d17_1101x668.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:668,&quot;width&quot;:1101,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:206589,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/195254231?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F480f1a53-261e-47d5-b824-449a3e446d17_1101x668.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!5TZk!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F480f1a53-261e-47d5-b824-449a3e446d17_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!5TZk!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F480f1a53-261e-47d5-b824-449a3e446d17_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!5TZk!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F480f1a53-261e-47d5-b824-449a3e446d17_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!5TZk!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F480f1a53-261e-47d5-b824-449a3e446d17_1101x668.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; What's the first thing you do when you get a new system design task? <br>&#128172; Have you ever discovered a critical requirement after the architecture was locked? <br>&#128172; Discover first or design first &#8212; how does your team actually work?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/how-you-build-the-new-workload/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/how-you-build-the-new-workload/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾 Stop designing architecture in a vacuum🐾]]></title><description><![CDATA[&#129299; Some of the worst architectures I&#8217;ve seen were built by brilliant engineers. I&#8217;m not talking about juniors making beginner mistakes &#8212; I mean senior, experienced people who genuinely know their craft. They could explain CAP theorem, debate consistency models, and design a multi-region failover in their sleep. And yet the system they delivered didn&#8217;t work for the people using it.]]></description><link>https://annpastushko.substack.com/p/stop-designing-architecture-in-a</link><guid isPermaLink="false">https://annpastushko.substack.com/p/stop-designing-architecture-in-a</guid><pubDate>Mon, 13 Apr 2026 13:00:52 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!7mqK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8952ea0-416e-47db-adae-767ac630db03_1101x668.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; Some of the worst architectures I&#8217;ve seen were built by brilliant engineers. I&#8217;m not talking about juniors making beginner mistakes &#8212; I mean senior, experienced people who genuinely know their craft. They could explain CAP theorem, debate consistency models, and design a multi-region failover in their sleep. And yet the system they delivered didn&#8217;t work for the people using it.</p><p>I spent a long time trying to figure out why this keeps happening. The technical skill was clearly there. The AWS services were solid choices. The diagrams looked clean.  But the systems still failed to meet the business need. I found one thing they all had in common: <strong>the design happened in a vacuum</strong>. No structured discovery. No explicit assumptions. No trade-off assessment. Just engineers solving the problem they imagined, not the problem that existed.</p><p><strong>TL;DR:</strong> Brilliant engineers can build wrong systems when the design process skips the hard questions. Technical skill doesn&#8217;t protect you from building the right solution to the wrong problem.</p><div><hr></div><h4>&#10060; Picking the storage layer before understanding access patterns</h4><p>A team built a multi-tenant analytics platform on DynamoDB. Solid choice on the surface &#8212; they needed scale, low latency, and serverless. The schema looked thoughtful: single-table design, composite keys, GSIs for the main queries. But six months after launch, their customers were asking for ad-hoc filtering, date range queries across tenant data, and CSV exports. None of that was in scope during design. DynamoDB doesn&#8217;t do ad-hoc well &#8212; you either know your access patterns upfront or you pay for it later.</p><p>They ended up bolting on Athena over S3 exports, which solved it &#8212; but added operational complexity, export lag, and cost that wasn&#8217;t in the original TCO.</p><p>The access patterns were knowable. Nobody asked. The storage choice was made before the stakeholders were interviewed properly.</p><blockquote><p><strong>Tip:</strong> <em>Before picking a NoSQL store, define your Core access patterns. If you expect more than ~30% of your queries to be unknown or ad-hoc in the first year, stick to Relational or build a robust export strategy from Day 1.</em></p></blockquote><h4>&#10060; Designing for scale you&#8217;ll never reach</h4><p>A startup building a B2B SaaS product designed their event pipeline for 10 million events per day from day one. Kinesis Data Streams, multiple Lambda consumers, DynamoDB for state &#8212; everything sized for a load they had no evidence was coming. Infrastructure costs were $4,000/month before a single paying customer.</p><p>Eighteen months later, they had 40 customers and 200K events per day. The pipeline was 98% idle. They were also hitting a wall nobody anticipated: their customers wanted synchronous webhooks with retry guarantees, not async event streams. The architecture was built for the wrong scale and the wrong integration model.</p><p>Meanwhile another team &#8212; same domain, similar product &#8212; started with SQS and a single Lambda consumer. When load grew, they scaled with a few config changes. When customers asked for webhooks, they added an outbound processor on the same queue. Their infrastructure was $300/month at the same customer count.</p><p>The first team had a technical debate about throughput. Nobody had a structured conversation about what load was actually expected, what customers actually needed at the integration layer, and what the cost envelope looked like.</p><blockquote><p><strong>Tip:</strong> Separate the scale conversation from the architecture conversation. Validate your load assumptions before you commit to the infrastructure that supports them. If you can&#8217;t get real data, define a range and design for the lower end first, but ensure a clear promotion path to scale up without a total rewrite.</p></blockquote><h4>&#10060; Skipping the non-functional requirements until it&#8217;s too late</h4><p>A team migrated a healthcare scheduling system to AWS. The migration was technically &#8220;perfect&#8221;&#8212;clean IaC, multi-AZ RDS, and a well-structured API Gateway + Lambda stack.</p><p>Three weeks post-launch, Compliance flagged a HIPAA requirement: every <strong>read</strong> of patient data had to be logged with the specific user&#8217;s identity and the data accessed. Not just &#8220;who logged in,&#8221; but &#8220;who saw what.&#8221;</p><p>The system had no mechanism for this. The Lambda functions were calling RDS directly with no shared data-access layer or middleware. Adding this wasn&#8217;t a &#8220;config change&#8221;&#8212;it meant instrumenting every function, restructuring the data access logic, and building a separate, immutable audit store. It cost them four weeks of rework on a &#8220;finished&#8221; system.</p><p>HIPAA wasn&#8217;t a surprise; it was a known constraint. But it was never surfaced during design because nobody ran a structured session to ask about audit obligations before the architecture was locked.</p><blockquote><p><strong>Tip:</strong> Non-functional requirements &#8212; compliance, availability SLAs, data retention, audit obligations &#8212; must be collected before Design starts, not discovered after launch. They change the architecture, not just the implementation.</p></blockquote><div><hr></div><p>None of these failures required better engineers. They required a better process &#8212; one that forces the hard questions before any service is chosen: What problem are we actually solving? Who are the real users and what do they need? What are the constraints we can't violate? What assumptions are we making, and what happens if they're wrong? </p><p>The architecture isn&#8217;t designed in a vacuum. It&#8217;s designed around a clear problem, with explicit trade-offs, and documented assumptions that anyone can review. I&#8217;ve been working on formalizing this kind of structured approach for architecture delivery &#8212; from first stakeholder conversation to handover. More on that soon.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!7mqK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8952ea0-416e-47db-adae-767ac630db03_1101x668.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!7mqK!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8952ea0-416e-47db-adae-767ac630db03_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!7mqK!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8952ea0-416e-47db-adae-767ac630db03_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!7mqK!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8952ea0-416e-47db-adae-767ac630db03_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!7mqK!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8952ea0-416e-47db-adae-767ac630db03_1101x668.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!7mqK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8952ea0-416e-47db-adae-767ac630db03_1101x668.png" width="1101" height="668" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c8952ea0-416e-47db-adae-767ac630db03_1101x668.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:668,&quot;width&quot;:1101,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:388777,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/193564718?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8952ea0-416e-47db-adae-767ac630db03_1101x668.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!7mqK!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8952ea0-416e-47db-adae-767ac630db03_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!7mqK!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8952ea0-416e-47db-adae-767ac630db03_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!7mqK!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8952ea0-416e-47db-adae-767ac630db03_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!7mqK!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8952ea0-416e-47db-adae-767ac630db03_1101x668.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Have you ever shipped something technically solid that still missed the mark? <br>&#128172; Discovery first or design first &#8212; how does your team actually work? <br>&#128172; What question do you always ask before picking a service?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/stop-designing-architecture-in-a/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/stop-designing-architecture-in-a/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾 SQL vs NoSQL: Do you ask the right question?🐾]]></title><description><![CDATA[&#129299; The SQL vs NoSQL debate is still hot in 2026. Modern PostgreSQL, Oracle, and MySQL added native JSON features &#8212; you store data as normalized tables, you access and update it as aggregated JSON documents. So should we all switch to it? When engineers say &#8220;our data is document-shaped,&#8221; they usually mean one of two things: the data model is hierarchical, or the access pattern returns aggregated objects. These are different problems &#8212; and they have different solutions.]]></description><link>https://annpastushko.substack.com/p/sql-vs-nosql-do-you-ask-the-right</link><guid isPermaLink="false">https://annpastushko.substack.com/p/sql-vs-nosql-do-you-ask-the-right</guid><pubDate>Mon, 30 Mar 2026 13:04:01 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_92N!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47a15835-032e-4bdf-bb8e-83a90c12fa61_1101x668.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; The SQL vs NoSQL debate is still hot in 2026. Modern PostgreSQL, Oracle, and MySQL added native JSON features &#8212; you store data as normalized tables, you access and update it as aggregated JSON documents. So should we all switch to it? When engineers say &#8220;our data is document-shaped,&#8221; they usually mean one of two things: the data model is hierarchical, or the access pattern returns aggregated objects. These are different problems &#8212; and they have different solutions.</p><p><strong>TL;DR:</strong> SQL handles a significant subset of document use cases well. The gap between relational and document stores narrowed significantly. The choice between relational and document stores is now less about data shape and more about scale, write throughput, and schema change velocity.</p><div><hr></div><h4>When SQL started winning</h4><p>The workloads where relational databases caught up share an equally clear pattern.</p><ul><li><p><strong>The data model is stable, but the access pattern is not.</strong> This often happens at a specific product lifecycle moment: the schema has settled, the team understands the domain, and operational requirements like transactions and integrity start mattering more than flexibility did early on.</p></li><li><p><strong>Consistency actually matters, but was being sacrificed for convenience.</strong> A significant amount of NoSQL adoption was driven by developer experience &#8212; joins felt painful, ORMs added friction, schema migrations felt risky. Not by genuine technical constraints. Those are solvable problems that don&#8217;t require giving up ACID guarantees.</p></li><li><p><strong>The system needs both integrity and flexibility.</strong> Analytics, reporting, referential constraints, and complex queries sitting alongside document-style application workloads. Previously that required two separate systems. Storing data relationally while accessing it as JSON collapses it into one &#8212; and the tooling ecosystem around SQL (BI tools, analytics, observability) is still heavily SQL-first. That operational reality matters.</p></li><li><p><strong>The scale is real but not extreme.</strong> Hundreds of millions of records, moderate write throughput, occasional spikes &#8212; this describes a large share of production systems. Not Netflix, not Uber. The operational complexity of running a dedicated document store at this scale is rarely justified by what you get back.</p></li></ul><p>PostgreSQL&#8217;s JSONB has been production-ready for years. Some teams are re-evaluating earlier NoSQL choices &#8212; particularly SaaS companies that adopted MongoDB before their consistency and integrity requirements solidified. Not because document stores were wrong, but because the original constraints that justified them no longer dominate.</p><div><hr></div><h4>When document stores still win</h4><p>The workloads where document stores genuinely outperform relational databases share a few structural characteristics.</p><ul><li><p><strong>Schema evolves faster than you can handle.</strong> When your data model changes multiple times per week across hundreds of millions of records &#8212; new attributes, different shapes per entity type, heterogeneous structures within the same collection &#8212; relational schema management becomes an operational bottleneck.</p></li><li><p><strong>Write throughput exceeds what relational can absorb.</strong> At very high write throughput, coordination overhead &#8212; locking, indexing, transactions &#8212; can become a bottleneck. Document stores and wide-column databases are designed to avoid it. This is workload-specific, not an inherent weakness of relational databases, but at the extreme end the gap is real.</p></li><li><p><strong>Consistency can be relaxed by design.</strong> Some systems explicitly choose eventual consistency to gain availability and partition tolerance. That&#8217;s not a limitation or workaround &#8212; it&#8217;s an architectural decision. Relational databases with ACID guarantees aren&#8217;t the right tool when you&#8217;ve deliberately chosen a different consistency model.</p></li><li><p><strong>Data is truly heterogeneous at scale.</strong> Not &#8220;our objects have nested arrays&#8221; &#8212; that&#8217;s JSON-in-SQL territory. But genuinely sparse, unpredictable attribute sets across billions of records, where a fixed schema adds no value and adds real storage overhead.</p></li></ul><p>These patterns rarely appear in isolation. The strongest cases for document stores &#8212; Netflix, Uber, Amazon internal systems &#8212; sit at the intersection of several simultaneously. A single pattern alone is worth examining carefully before it justifies the operational overhead.</p><p>One axis worth considering: running MongoDB or Cassandra clusters in production is operationally heavier than managed relational databases. Backup strategies, consistency guarantees, debugging query performance on deeply nested JSON &#8212; these are real costs that rarely appear in architecture discussions but show up in engineering hours.</p><div><hr></div><h4>The actual decision space</h4><p>The database choice is rarely wrong at the start &#8212; it becomes wrong as the system evolves. Early-stage products have uncertain schemas, unclear access patterns, and low operational maturity. Document stores are genuinely lower friction here. The problem is that early decisions get locked in. By the time transactional integrity, complex queries, and referential constraints become real requirements, migration is expensive.</p><p>Migration is hardest when your data model and access patterns diverge from your storage model &#8212; regardless of which direction you&#8217;re moving. This is worth factoring in at the start, not after the system has grown around its database.</p><p>The constraints worth evaluating honestly:</p><ul><li><p><strong>Scale, write throughput, schema velocity at the extreme end?</strong> &#8212; Document stores were designed for this, and the gap is unlikely to close soon.</p></li><li><p><strong>Consistency and integrity requirements that grow over time?</strong> &#8212; They almost always do. Relational databases age better here.</p></li><li><p><strong>Complex queries on nested structures?</strong> &#8212; JSON in SQL is not equivalent to native document indexing. But the deeper issue is that complexity doesn't disappear &#8212; it shifts. With document stores, complexity lives in the schema design. With JSON in relational databases, it moves into the query layer. Schema is explicit and auditable. Query logic that encodes structural assumptions is harder to catch and harder to maintain.</p></li><li><p><strong>Early stage, schema still uncertain?</strong> &#8212; Both are viable. The question is which direction is cheaper to reverse if requirements change.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!_92N!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47a15835-032e-4bdf-bb8e-83a90c12fa61_1101x668.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!_92N!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47a15835-032e-4bdf-bb8e-83a90c12fa61_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!_92N!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47a15835-032e-4bdf-bb8e-83a90c12fa61_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!_92N!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47a15835-032e-4bdf-bb8e-83a90c12fa61_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!_92N!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47a15835-032e-4bdf-bb8e-83a90c12fa61_1101x668.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!_92N!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47a15835-032e-4bdf-bb8e-83a90c12fa61_1101x668.png" width="1101" height="668" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/47a15835-032e-4bdf-bb8e-83a90c12fa61_1101x668.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:668,&quot;width&quot;:1101,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:147882,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/191134617?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47a15835-032e-4bdf-bb8e-83a90c12fa61_1101x668.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!_92N!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47a15835-032e-4bdf-bb8e-83a90c12fa61_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!_92N!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47a15835-032e-4bdf-bb8e-83a90c12fa61_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!_92N!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47a15835-032e-4bdf-bb8e-83a90c12fa61_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!_92N!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F47a15835-032e-4bdf-bb8e-83a90c12fa61_1101x668.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Have you ever migrated away from a document store?<br>&#128172; Has your database choice ever become wrong as the product evolved?<br>&#128172; What pushed you to a document store when SQL wasn't enough?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/sql-vs-nosql-do-you-ask-the-right/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/sql-vs-nosql-do-you-ask-the-right/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾Behind the Architecture: GenAI chatbot🐾]]></title><description><![CDATA[&#129299; Recently, I participated in a discussion about the &#8220;ideal&#8221; architecture for a GenAI chatbot. We covered scalability, resilience, latency, model flexibility &#8212; and at some point it reminded me of an architecture I designed a while ago. By every standard we discussed, it wasn&#8217;t ideal. So I started thinking: why did I build it that way? And was it actually wrong?]]></description><link>https://annpastushko.substack.com/p/behind-the-architecture-genai-chatbot</link><guid isPermaLink="false">https://annpastushko.substack.com/p/behind-the-architecture-genai-chatbot</guid><pubDate>Mon, 16 Mar 2026 14:02:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!5bD2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc05d4c74-4213-471a-965c-7f268cbe6df8_1101x668.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; Recently, I participated in a discussion about the &#8220;ideal&#8221; architecture for a GenAI chatbot. We covered scalability, resilience, latency, model flexibility &#8212; and at some point it reminded me of an architecture I designed a while ago. By every standard we discussed, it wasn&#8217;t ideal. So I started thinking: why did I build it that way? And was it actually wrong?</p><p><strong>TL;DR: </strong>Every architecture is a product of its context. There are no bad architectures, only expensive ones. <em>(inspired by Mark Richards and Neal Ford)</em></p><h4><strong>The Ideal Architecture</strong></h4><p>A production-grade GenAI chatbot should be highly available, scalable, and resilient. Responses should stream token by token. It should handle multiple models and scale efficiently under load.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!5bD2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc05d4c74-4213-471a-965c-7f268cbe6df8_1101x668.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!5bD2!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc05d4c74-4213-471a-965c-7f268cbe6df8_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!5bD2!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc05d4c74-4213-471a-965c-7f268cbe6df8_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!5bD2!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc05d4c74-4213-471a-965c-7f268cbe6df8_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!5bD2!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc05d4c74-4213-471a-965c-7f268cbe6df8_1101x668.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!5bD2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc05d4c74-4213-471a-965c-7f268cbe6df8_1101x668.png" width="1101" height="668" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c05d4c74-4213-471a-965c-7f268cbe6df8_1101x668.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:668,&quot;width&quot;:1101,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:168504,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/190421581?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc05d4c74-4213-471a-965c-7f268cbe6df8_1101x668.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!5bD2!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc05d4c74-4213-471a-965c-7f268cbe6df8_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!5bD2!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc05d4c74-4213-471a-965c-7f268cbe6df8_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!5bD2!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc05d4c74-4213-471a-965c-7f268cbe6df8_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!5bD2!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc05d4c74-4213-471a-965c-7f268cbe6df8_1101x668.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>On AWS this means: </p><ul><li><p><strong>Frontend &amp; Delivery </strong>&#8212; CloudFront and S3 serve the UI globally with edge caching. An ALB acts as the entry point, supporting HTTP Chunked Transfer Encoding to deliver a "typewriter" streaming experience to the client with minimal TTFT (Time to First Token).</p></li><li><p><strong>Backend</strong> &#8212; ECS serves as the "brain," managing prompt templates and RAG logic in a container environment. It retrieves conversation state from ElastiCache and invokes Bedrock using streaming APIs, bypassing API Gateway timeouts for long-running reasoning tasks.</p></li><li><p><strong>RAG Pipeline</strong> &#8212; S3 stores source documents. When new documents arrive, Lambda  generates embeddings and stores them in OpenSearch for semantic search (or Aurora (pgvector) if the team prefers SQL). SQS decoupling ensures ingestion doesn't bottleneck during bulk uploads. At query time, the most relevant context is retrieved and passed to the model.</p></li><li><p><strong>Observability</strong> &#8212; CloudWatch and X-Ray provide logs, metrics, and distributed tracing across all components.</p></li><li><p><strong>Resilience</strong> &#8212; Multi-AZ database, ECS auto-scaling, and dead letter queues ensure the system handles failures gracefully.</p></li><li><p><strong>Security</strong> &#8212; Bedrock Guardrails provide native PII redaction and content filtering.</p></li></ul><p>Robust. But also complex, expensive, and weeks of work to do right.</p><h4><strong>My Architecture</strong></h4><p>This was a presale demo tool &#8212; deployed into a client&#8217;s AWS account for hours or a couple of days, then torn down. The goal: show that RAG works, that clients could connect their own knowledge base and get relevant answers, using either third-party models via API or their own models hosted on SageMaker endpoints. Their data, their models, their account &#8212; that&#8217;s a trust argument in a sales conversation, not just a technical detail.</p><p>The entire infrastructure was provisioned with Terraform, which made it repeatable and deployable in any AWS account without manual steps.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!CNms!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c97f317-1988-4b2f-bc9a-6d08f849acfc_1101x668.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!CNms!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c97f317-1988-4b2f-bc9a-6d08f849acfc_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!CNms!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c97f317-1988-4b2f-bc9a-6d08f849acfc_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!CNms!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c97f317-1988-4b2f-bc9a-6d08f849acfc_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!CNms!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c97f317-1988-4b2f-bc9a-6d08f849acfc_1101x668.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!CNms!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c97f317-1988-4b2f-bc9a-6d08f849acfc_1101x668.png" width="1101" height="668" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9c97f317-1988-4b2f-bc9a-6d08f849acfc_1101x668.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:668,&quot;width&quot;:1101,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:163136,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/190421581?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c97f317-1988-4b2f-bc9a-6d08f849acfc_1101x668.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!CNms!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c97f317-1988-4b2f-bc9a-6d08f849acfc_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!CNms!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c97f317-1988-4b2f-bc9a-6d08f849acfc_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!CNms!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c97f317-1988-4b2f-bc9a-6d08f849acfc_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!CNms!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c97f317-1988-4b2f-bc9a-6d08f849acfc_1101x668.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The system had several EC2 instances running containers: UI, backend, and database connector. RDS with pgvector served as the vector store.</p><p>Now let me explain what this architecture deliberately doesn&#8217;t have &#8212; and why:</p><ul><li><p><strong>No auto-scaling</strong> (Scalability) &#8212; we knew the load: one client, a few people in a room. Scaling adds complexity and cost with zero benefit for a two-hour demo.</p></li><li><p><strong>No Multi-AZ Failover </strong>(Reliability) &#8212; if something breaks during a demo we fix it manually. The cost of Multi-AZ for an ephemeral environment makes no sense.</p></li><li><p><strong>No streaming</strong> &#8212; in a presale, a correct and relevant answer matters more than how it appears on screen. Streaming is a UX improvement for production. Implementing streaming adds significant development overhead and introduces potential points of failure in the frontend-backend handshake.</p></li><li><p><strong>No dedicated vector database</strong> &#8212; OpenSearch is overkill for a handful of demo documents. We used RDS with pgvector because it was faster to develop with a team that already knew SQL, easier to manage, and significantly cheaper for a short-lived environment.</p></li><li><p><strong>No Aurora Serverless</strong> &#8212; it was explicitly rejected because it<strong> </strong>scales to zero which means cold start. For a two-hour demo that&#8217;s unacceptable.</p></li><li><p><strong>No centralized logging</strong> &#8212; setting up CloudWatch Logs Insights or X-Ray for a single-node, two-hour demo is overkill. We relied on standard Docker logs for real-time debugging, prioritizing speed of deployment over long-term operational monitoring.</p></li></ul><p>The requirements were clear: build fast, design for portability and ephemerality. Every decision flowed from that. Not an ideal architecture &#8212; but a perfect one for this context.</p><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Would you like to see more posts like this?<br>&#128172; What constraints shaped your last architecture decision?<br>&#128172; What was the hardest trade-off you made on a tight deadline?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/behind-the-architecture-genai-chatbot/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/behind-the-architecture-genai-chatbot/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[ 🐾EventBridge Pipes: Stop writing Lambda Glue code 🐾]]></title><description><![CDATA[&#129299; EventBridge Pipes launched at re:Invent 2022, picked up customer-managed KMS keys and enhanced logging in 2024, and most teams still don&#8217;t know it exists.]]></description><link>https://annpastushko.substack.com/p/eventbridge-pipes-stop-writing-lambda</link><guid isPermaLink="false">https://annpastushko.substack.com/p/eventbridge-pipes-stop-writing-lambda</guid><pubDate>Mon, 02 Mar 2026 14:02:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!q8aF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80ee9c58-58c8-4b9a-8544-9cf3f330f08a_1101x668.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; EventBridge Pipes launched at re:Invent 2022, picked up customer-managed KMS keys and enhanced logging in 2024, and most teams still don&#8217;t know it exists. They&#8217;re writing Lambda functions just to move data between services &#8212; SQS&#8594;Lambda&#8594;SNS, DynamoDB Streams&#8594;Lambda&#8594;EventBridge. That Lambda does nothing except reshape a payload and forward it. Pipes eliminates this pattern entirely.</p><p><strong>TL;DR:</strong> Pipes gives you point-to-point integrations between AWS services without glue code. It wins on operational simplicity, and often on cost. The catch: once your transformation logic gets complex, you&#8217;re partially back to Lambda anyway.</p><h4>&#9989; Replace your passthrough Lambdas</h4><p>The classic glue pattern: a Lambda reads from a source, does minimal transformation, writes to a target. You&#8217;re paying for cold starts, managing deployments, writing error handling for a function with zero business logic, and getting paged when it breaks at 2am. Pipes replaces this with a fully managed pipeline: Source &#8594; optional filter &#8594; optional enrichment &#8594; target. No function to deploy, no CloudWatch logs to sift through for something that does nothing interesting. <strong>If your Lambda&#8217;s entire job is reading from one AWS service and writing to another &#8212; that&#8217;s a Pipe.</strong></p><blockquote><p><strong>Tip:</strong> Audit your Lambda inventory for functions under 30 lines that only call AWS SDK methods. Those are Pipe candidates.</p></blockquote><h4>&#9989; Filter at the source, not inside your function</h4><p>With SQS&#8594;Lambda, every message triggers your function &#8212; including ones you immediately discard. Pipes filtering happens <em>before</em> invocation. Messages that don&#8217;t match never reach your target. For a Kinesis stream processing 10M events/day where 85% are irrelevant: that&#8217;s 8.5M Lambda invocations eliminated. At standard pricing (128MB, 200ms average): ~$34/month saved on invocations alone, before compute time.</p><p>Pipes charges $0.40 per 1M events &#8212; but filtering is free. Enrichment and target delivery are what's charged. If 85% of your Kinesis stream is filtered out, you only pay for the 15% that reaches the target.</p><blockquote><p><strong>Tip:</strong> Define filter criteria at the Pipe source first. Even a single attribute match can cut downstream costs significantly.</p></blockquote><h4>&#9989; Enrichment without a full Lambda</h4><p>Pipes has a built-in enrichment step: call a Lambda, API Gateway, or Step Functions to augment the event before it reaches the target. The difference from the old pattern is intent &#8212; enrichment is a deliberate step in a managed pipeline, not glue holding two services together.</p><p>This matters when you need <em>some</em> logic &#8212; a database lookup, an external API call &#8212; but don&#8217;t want a standalone function managing the entire routing lifecycle.</p><blockquote><p><strong>Tip:</strong> Use enrichment for data augmentation, not transformation logic. If your enrichment function is doing routing decisions, reconsider the design.</p></blockquote><h4><strong>&#9888;&#65039;Production gotchas:</strong></h4><ul><li><p><strong>No managed re-drive</strong> &#8212; unlike SQS, you need to build your own replay mechanism for failed events</p></li><li><p><strong>DLQ retention mismatch</strong> &#8212; DynamoDB Streams keep records for 24hrs; if events sit in DLQ longer, replay is impossible</p></li><li><p><strong>Batch retry behavior</strong> &#8212; one failed record retries the entire batch; without idempotency on the target, you&#8217;ll process records multiple times</p></li><li><p><strong>PrivateLink required for VPC sources</strong> &#8212; easy to miss until your Pipe silently can&#8217;t reach the source</p></li><li><p><strong>Throughput limits aren&#8217;t obvious</strong> &#8212; Pipes adds its own concurrency limits on top of source limits; high-volume Kinesis streams can hit Pipes limits first</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!q8aF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80ee9c58-58c8-4b9a-8544-9cf3f330f08a_1101x668.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!q8aF!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80ee9c58-58c8-4b9a-8544-9cf3f330f08a_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!q8aF!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80ee9c58-58c8-4b9a-8544-9cf3f330f08a_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!q8aF!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80ee9c58-58c8-4b9a-8544-9cf3f330f08a_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!q8aF!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80ee9c58-58c8-4b9a-8544-9cf3f330f08a_1101x668.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!q8aF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80ee9c58-58c8-4b9a-8544-9cf3f330f08a_1101x668.png" width="1101" height="668" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/80ee9c58-58c8-4b9a-8544-9cf3f330f08a_1101x668.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:668,&quot;width&quot;:1101,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:171309,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/181972366?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80ee9c58-58c8-4b9a-8544-9cf3f330f08a_1101x668.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!q8aF!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80ee9c58-58c8-4b9a-8544-9cf3f330f08a_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!q8aF!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80ee9c58-58c8-4b9a-8544-9cf3f330f08a_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!q8aF!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80ee9c58-58c8-4b9a-8544-9cf3f330f08a_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!q8aF!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F80ee9c58-58c8-4b9a-8544-9cf3f330f08a_1101x668.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Have you replaced Lambda glue code with EventBridge Pipes? Was it worth it? <br>&#128172; Hit the VTL wall and ended up adding Lambda enrichment anyway? <br>&#128172; Any production gotchas I missed?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/eventbridge-pipes-stop-writing-lambda/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/eventbridge-pipes-stop-writing-lambda/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾S3 Express One Zone: When you need it🐾]]></title><description><![CDATA[&#129299; S3 Express One Zone launched at re:Invent 2023 promising 10x faster access and 50% lower request costs. Even as prices evolve, it remains significantly more expensive than S3 Standard &#8212; currently around 5x the storage cost. Teams see &#8220;10x faster&#8221; and assume they need it. Let&#8217;s discover when you actually need it.]]></description><link>https://annpastushko.substack.com/p/s3-express-one-zone-when-you-need</link><guid isPermaLink="false">https://annpastushko.substack.com/p/s3-express-one-zone-when-you-need</guid><pubDate>Mon, 16 Feb 2026 14:02:35 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hijT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f41eb81-6e05-4f7a-b7d0-9558542914e9_1101x668.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; S3 Express One Zone launched at re:Invent 2023 promising 10x faster access and 50% lower request costs. Even as prices evolve, it remains significantly more expensive than S3 Standard &#8212; currently around 5x the storage cost. Teams see &#8220;10x faster&#8221; and assume they need it. Let&#8217;s discover when you actually need it.</p><div class="pullquote"><p><strong>TL;DR:</strong> Express shines when data is accessed constantly &#8212; multiple times per hour or more. If your data sits cold between jobs, you&#8217;re paying 5x for nothing.</p></div><h4>&#10060; Paying for speed when data sits idle</h4><div class="pullquote"><p>A team I worked with migrated 500GB of ML training data to Express after reading success stories. Storage jumped from $11.50/month to $55/month. Each training run took 3 days, but the data sat idle in Express for weeks between experiments. They were paying premium storage costs for data that spent 90% of its time unused.</p></div><p>Express makes sense when data is accessed constantly &#8212; multiple times per hour. If your data sits cold between jobs, you&#8217;re paying 5x more for nothing.</p><blockquote><p><strong>Tip:</strong> Use Express for hot data with frequent access. Move cold data back to Standard with lifecycle policies.</p></blockquote><h4>&#10060; Skipping the TCO math</h4><div class="pullquote"><p>I saw another team assumed &#8220;10x faster&#8221; automatically meant cost savings. They moved 2TB of analytics data to Express without calculating the full equation. With S3 Standard, their job took 45 minutes. Storage: $46/month. Requests: $200 per job (500K GETs). Compute: $27 for 45 minutes. With Express, storage jumped to $220/month but requests dropped to $15 per job and compute to $18 for 30 minutes.</p></div><p>For workloads running twice daily, monthly savings on requests and compute ($388) exceeded the storage premium ($174). Express won &#8212; but only because they ran the numbers.</p><blockquote><p><strong>Tip:</strong> Express beats Standard when (reduced compute cost + reduced request cost) &gt; storage premium. Calculate before migrating.</p></blockquote><h4>&#10060; Ignoring object size impacts</h4><p>AWS&#8217;s &#8220;50% cheaper requests&#8221; claim only applies to objects under 512KB. Above that, you pay additional per-GB charges.</p><div class="pullquote"><p>In one project, team migrated 10,000 files averaging 2MB each. They expected Express to cut request costs in half. Instead, Express charged $30 for base requests plus $120 in per-GB GET charges &#8212; $150 total. Better than Standard&#8217;s $400, but not the 50% reduction they expected.</p></div><p>When object sizes exceed 645KB, Express APIs become more expensive per byte than Standard. The sweet spot is frequent access to many small objects, not large files.</p><blockquote><p><strong>Tip:</strong> Express wins with small objects (&lt;512KB) accessed frequently. Large files accessed infrequently? Standard is cheaper.</p></blockquote><h4>&#10060; Storing critical data in a single AZ</h4><p>Express stores data in a single Availability Zone. If that AZ fails, your data is gone until it recovers.</p><div class="pullquote"><p>A team stored ML training checkpoints exclusively in Express&#8212;no backup. When an AZ issue corrupted their bucket, they lost 2 days of training progress. They learned that ephemeral training data needs durability too if recreating it is expensive.</p></div><p>The right approach is using Express as a cache layer with the source of truth in Standard. Analytics jobs can pull hot datasets into Express at job start, run queries, then delete the copies. If the AZ fails mid-job, reload from Standard.</p><blockquote><p><strong>Tip:</strong> Express works for caching or ephemeral data where AZ failure is tolerable. For anything you can&#8217;t afford to lose, keep the source in Standard.</p></blockquote><h4>&#10060; Underestimating the migration work</h4><p>Express uses directory buckets with session-based authentication instead of standard IAM. Your existing S3 code won&#8217;t work without changes&#8212;you need CreateSession calls before every operation.</p><div class="pullquote"><p>I saw how data team tried switching their Lambda functions from Standard to Express and hit immediate auth failures. They had to add session creation logic, handle session expiration, and implement session pooling. What they thought was a simple bucket swap turned into a two-week refactoring project.</p></div><p>Directory buckets also don&#8217;t support versioning, object lock, or replication. One team needed versioning for regulatory requirements and couldn&#8217;t use Express at all.</p><blockquote><p><strong>Tip:</strong> Budget time for code changes and verify your required S3 features are supported before committing.</p></blockquote><h4>&#10060; Using Express for continuous high-throughput workloads</h4><p><a href="https://jack-vanlightly.com/blog/2023/11/29/s3-express-one-zone-not-quite-what-i-hoped-for">Jack Vanlightly&#8217;s analysis</a> shows Express isn&#8217;t cheap enough to replace replicated cache implementations for transactional workloads. Systems like Neon and Kora use replicated write-ahead logs because replication costs scale with throughput, not request count. Express charges per request. </p><div class="pullquote"><p>One of the teams I worked with tried using Express for their database write-ahead log serving 10,000 writes per second. That&#8217;s 864 million requests per day. Request costs added up faster than running a 3-node replicated cache with EBS.</p></div><p>Express works for bursty analytics jobs with high-volume access patterns, but continuous high-throughput transactional systems still need traditional replication.</p><blockquote><p><strong>Tip:</strong> Express fits batch-oriented workloads. For 24/7 high-throughput transactional systems, replicated caches remain more cost-effective.</p></blockquote><h4>&#9989; When Express actually makes sense</h4><p>Express delivers ROI when it removes a performance bottleneck causing you to over-provision elsewhere:</p><ul><li><p><strong>I/O latency blocks expensive compute:</strong> ML training with frequent checkpointing, distributed processing where workers wait on S3, or batch jobs where compute sits idle during data access.</p></li><li><p><strong>User experience demands sub-second response:</strong> Interactive dashboards, real-time analytics, or applications with high-concurrency S3 reads where Standard&#8217;s 150-250ms latency creates noticeable delays.</p></li><li><p><strong>Faster S3 enables downstream optimization:</strong> When S3 latency bottlenecks your pipeline, forcing you to over-provision other infrastructure. Pinterest&#8217;s story perfectly illustrates this &#8212; faster S3 let them reduce MemQ brokers, cutting costs by 40% across the stack.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!hijT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f41eb81-6e05-4f7a-b7d0-9558542914e9_1101x668.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!hijT!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f41eb81-6e05-4f7a-b7d0-9558542914e9_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!hijT!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f41eb81-6e05-4f7a-b7d0-9558542914e9_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!hijT!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f41eb81-6e05-4f7a-b7d0-9558542914e9_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!hijT!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f41eb81-6e05-4f7a-b7d0-9558542914e9_1101x668.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!hijT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f41eb81-6e05-4f7a-b7d0-9558542914e9_1101x668.png" width="1101" height="668" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7f41eb81-6e05-4f7a-b7d0-9558542914e9_1101x668.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:668,&quot;width&quot;:1101,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:100994,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/181972406?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f41eb81-6e05-4f7a-b7d0-9558542914e9_1101x668.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!hijT!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f41eb81-6e05-4f7a-b7d0-9558542914e9_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!hijT!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f41eb81-6e05-4f7a-b7d0-9558542914e9_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!hijT!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f41eb81-6e05-4f7a-b7d0-9558542914e9_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!hijT!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7f41eb81-6e05-4f7a-b7d0-9558542914e9_1101x668.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Have you tried S3 Express One Zone? What workloads did you use it for?<br>&#128172; Did the TCO math work out in your favor, or did storage costs surprise you?<br>&#128172; Have you hit unexpected limitations with directory buckets or single-AZ durability?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/s3-express-one-zone-when-you-need/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/s3-express-one-zone-when-you-need/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾Do you really need single-table design for DynamoDB?🐾]]></title><description><![CDATA[&#129299; Single-table design has become the default recommendation for DynamoDB. Rick Houlihan's re:Invent talks turned it into gospel &#8212; teams now force everything into one table because "that's how you're supposed to do it." But after working with DynamoDB implementations for several years, I've seen this pattern cause  problems when applied blindly &#8212; operational costs often outweigh theoretical benefits.]]></description><link>https://annpastushko.substack.com/p/do-you-really-need-single-table-design</link><guid isPermaLink="false">https://annpastushko.substack.com/p/do-you-really-need-single-table-design</guid><pubDate>Mon, 02 Feb 2026 14:01:12 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!m8CE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff28a7686-631a-4d31-8142-46862e5b81ab_1101x668.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; Single-table design has become the default recommendation for DynamoDB. Rick Houlihan's re:Invent talks turned it into gospel &#8212; teams now force everything into one table because "that's how you're supposed to do it." But after working with DynamoDB implementations for several years, I've seen this pattern cause  problems when applied blindly &#8212; operational costs often outweigh theoretical benefits.</p><p>AWS&#8217;s recent documentation emphasizes &#8220;consider your operational needs&#8221; and warns about added complexity. It&#8217;s less &#8220;always single-table&#8221; and more &#8220;use it when access patterns align.&#8221;</p><h4>&#10060; Lost operational flexibility</h4><p>Single-table design locks you into one configuration for all data types. Every entity shares the same capacity mode, backup schedule, table class, and TTL settings.</p><p>I worked with a team that had user profiles needing on-demand capacity for spikes, plus historical analytics that could use provisioned capacity. Single table forced on-demand for everything. The monthly bill was 3x higher than needed. Another team needed hourly backups for transactions, but only daily for audit logs. A single table meant backing up everything hourly or risking data loss. Storage costs doubled.</p><blockquote><p><strong>Tip:</strong> Separate tables let you use on-demand for hot data and Standard-IA class for cold data at 50% savings.</p></blockquote><h4>&#10060; The observability nightmare</h4><p>You can&#8217;t CloudWatch alarm on specific entity types &#8212; when read capacity spikes, you&#8217;re guessing which entity caused it.</p><p>S3 exports also become painful. One team wanted to analyze user behavior, but had to export everything &#8212; users, orders, inventory, and sessions. Each export took 45 minutes and cost $8. Table scans are even worse; migrating one entity type means scanning and paying for millions of irrelevant items.</p><blockquote><p><strong>Tip:</strong> Separate tables give entity-level metrics and exports. Alarm on user table throttles specifically and export only what you need.</p></blockquote><h4>&#10060; Development complexity tax</h4><p>New developers take weeks to understand single-table schemas. Every entity needs composite keys: <em>User-{id}, Order-{id}, Inventory-{sku}</em>. You end up with <em>GSI1PK</em> and <em>GSI2SK</em> everywhere.</p><p>One team spent two weeks adding a &#8220;query by order status&#8221; feature. They needed a new GSI and had to migrate all items to include the new key, coordinating deployment across services. With separate tables, adding a GSI is one simple schema change on one table. No migration, no coordination.</p><h4>&#10060; When parallel requests beat complex queries</h4><p>Single-table design is optimized for fetching related data in one query. But modern frontends fire parallel requests &#8212; and two simple queries might be faster than one complex query with heavy filtering.</p><p>A team I worked with had a dashboard showing user details plus recent orders. The single-table query was complex: fetch user by PK, query GSI1 for orders, then filter by date range. Total latency: 120ms. We tested splitting it into two separate tables with parallel requests. User fetch: 40ms. Orders fetch: 45ms. Total latency: 50ms because the frontend made both calls simultaneously. Your mileage will vary &#8212; it depends on backend orchestration and whether you need atomic reads.</p><blockquote><p><strong>Tip:</strong> Network latency is low. Two parallel 50ms queries often beat one 120ms query &#8212; especially since you can cache them independently.</p></blockquote><h4>&#10060; The 10GB partition limit</h4><p>If you choose to use Local Secondary Indexes, DynamoDB limits item collections to 10GB per partition key. Not all single-table designs use LSIs, but when they do, the concentrated data makes this limit easier to hit.</p><p>One team stored user data with LSIs for sorting. When active users exceeded 10GB of data (including all related items), writes started failing. They had to partition users across multiple &#8220;user shard&#8221; keys &#8212; adding complexity that multiple tables would have avoided entirely.</p><h4>&#10060; GraphQL&#8217;s contradiction</h4><p>STD forces you into &#8220;lookahead&#8221; resolvers to fetch nested data in one query. I saw a team turn a simple field addition into a week-long project because the lookahead logic and query builders were so tightly coupled.</p><blockquote><p><strong>Tip:</strong> Simple resolvers with separate tables lead to cleaner, more maintainable code than complex &#8220;all-in-one&#8221; queries.</p></blockquote><h4>&#9989; When single-table actually makes sense</h4><p>Single-table design works well for tightly coupled domains with stable access patterns. If you&#8217;re building a multi-tenant SaaS where tenant isolation is critical, or your entities are always queried together (user + preferences + settings), single-table can be the right choice.</p><p>The problem isn&#8217;t the pattern itself &#8212; it&#8217;s treating it as the default instead of a tool for specific cases.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!m8CE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff28a7686-631a-4d31-8142-46862e5b81ab_1101x668.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!m8CE!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff28a7686-631a-4d31-8142-46862e5b81ab_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!m8CE!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff28a7686-631a-4d31-8142-46862e5b81ab_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!m8CE!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff28a7686-631a-4d31-8142-46862e5b81ab_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!m8CE!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff28a7686-631a-4d31-8142-46862e5b81ab_1101x668.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!m8CE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff28a7686-631a-4d31-8142-46862e5b81ab_1101x668.png" width="1101" height="668" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f28a7686-631a-4d31-8142-46862e5b81ab_1101x668.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:668,&quot;width&quot;:1101,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:108271,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/181972440?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff28a7686-631a-4d31-8142-46862e5b81ab_1101x668.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!m8CE!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff28a7686-631a-4d31-8142-46862e5b81ab_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!m8CE!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff28a7686-631a-4d31-8142-46862e5b81ab_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!m8CE!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff28a7686-631a-4d31-8142-46862e5b81ab_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!m8CE!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff28a7686-631a-4d31-8142-46862e5b81ab_1101x668.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Have you been forced into single-table design when separate tables would have been simpler?<br>&#128172; What's your take on the development complexity vs runtime performance?<br>&#128172; Have you hit the 10GB partition limit?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/do-you-really-need-single-table-design/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/do-you-really-need-single-table-design/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾Building Safety into GenAI: What Actually Works🐾]]></title><description><![CDATA[&#129299; Implementing GenAI guardrails is easy; implementing them without breaking your performance SLAs is the hard part.]]></description><link>https://annpastushko.substack.com/p/building-safety-into-genai-what-actually</link><guid isPermaLink="false">https://annpastushko.substack.com/p/building-safety-into-genai-what-actually</guid><pubDate>Mon, 19 Jan 2026 14:03:04 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xgul!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ee1a167-b09f-40d1-89aa-a2bd8d7b4034_1101x668.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; Implementing GenAI guardrails is easy; implementing them without breaking your performance SLAs is the hard part. With the recent rollout of Automated Reasoning and AgentCore updates, Amazon Bedrock has significantly changed how we enforce safety and grounding in multi-step agent workflows.</p><p>This post breaks down how these new structural components actually fit into a your architecture. We&#8217;ll look at the shift toward multi-layer enforcement, the trade-offs of centralized policy management, and the practical constraints &#8212; from latency hits to Cedar policy limitations &#8212; that determine whether these tools are actually viable for your current stack.</p><h4>1&#65039;&#8419;Three-Layer Guardrail Enforcement</h4><p>Instead of a single on/off switch, guardrails work best when applied at multiple stages of the request lifecycle:</p><ul><li><p><strong>Input Layer:</strong> Evaluate user input before model invocation to block prompt injection and detect PII. This reduces unnecessary model calls and expensive RAG retrievals.</p></li><li><p><strong>Processing Layer (AgentCore):</strong> Enforces tool-use policies for agents at runtime. If an agent attempts to invoke a <code>delete_user</code> action but the policy allows only read access, the runtime blocks the call.<br>&#9888;&#65039; This layer controls capabilities, not content.</p></li><li><p><strong>Output Layer:</strong> Validates model outputs for PII and hallucination signals before returning results to the user or downstream systems.</p></li></ul><p><strong>Trade-offs:</strong> Each layer adds latency, with output checks being the most expensive. Use selectively for high-risk outputs.</p><h4>2&#65039;&#8419;Centralized Guardrail Application Across Models</h4><p>The <code>ApplyGuardrail</code> API enables consistent guardrail enforcement across multiple model backends:</p><ul><li><p>Native Bedrock models (Claude, Llama)</p></li><li><p>External APIs (OpenAI, Anthropic) via a Lambda wrapper</p></li><li><p>Self-hosted models on SageMaker</p></li></ul><p><strong>Why it matters:</strong> Policies remain consistent when switching or combining model providers. <strong><br></strong>&#9888;&#65039; Enforcement for non-Bedrock models depends on your integration layer, which may add latency and reduce observability.</p><p><strong>Trade-offs:</strong> Increases &#8220;Time to First Token&#8221; due to double-hop network calls (Guardrail API + Model API); centralized governance creates a single point of failure for all model traffic.</p><h4>3&#65039;&#8419;RAG Verification Strategy</h4><p>RAG systems fail differently from standard LLMs. This concept combines two key safeguards:</p><ul><li><p><strong>Contextual Grounding:</strong> Ensures generated answers are supported by retrieved documents rather than being purely generative. Accuracy is strongest for English and well-structured sources.</p></li><li><p><strong>Automated Reasoning:</strong> Applies formal logic (SMT solvers) to verify output correctness in regulated use cases (finance, legal, compliance). <strong><br>&#9888;&#65039; </strong>~99% accuracy claims apply only to formalizable reasoning tasks, not open-ended text.</p></li></ul><p><strong>Trade-offs:</strong> Computationally expensive; only useful when formal verification is required.</p><h4>&#128681; Production Gotchas</h4><ul><li><p><strong>Limited explainability:</strong> Bedrock Guardrails do not provide detailed reasons for blocks, complicating false-positive debugging.</p></li><li><p><strong>Service quotas:</strong> Default RPM/TPM limits may be insufficient for production workloads.</p></li><li><p><strong>Cedar policy constraints:</strong> No floating-point arithmetic or advanced regex, which can be limiting for fine-grained financial/technical rules.</p></li><li><p><strong>Non-English performance:</strong> Contextual grounding primarily supports English; performance degrades significantly for other languages.</p></li><li><p><strong>DLQ pattern:</strong> Route blocked requests to an SQS DLQ for analysis instead of returning generic 4xx errors.</p></li><li><p><strong>Versioning:</strong> Avoid <code>LATEST</code>. Treat guardrails as versioned, immutable infrastructure &#8212; validate in shadow environments before promotion.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xgul!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ee1a167-b09f-40d1-89aa-a2bd8d7b4034_1101x668.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xgul!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ee1a167-b09f-40d1-89aa-a2bd8d7b4034_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!xgul!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ee1a167-b09f-40d1-89aa-a2bd8d7b4034_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!xgul!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ee1a167-b09f-40d1-89aa-a2bd8d7b4034_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xgul!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ee1a167-b09f-40d1-89aa-a2bd8d7b4034_1101x668.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xgul!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ee1a167-b09f-40d1-89aa-a2bd8d7b4034_1101x668.png" width="1101" height="668" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8ee1a167-b09f-40d1-89aa-a2bd8d7b4034_1101x668.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:668,&quot;width&quot;:1101,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:204159,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/181972480?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ee1a167-b09f-40d1-89aa-a2bd8d7b4034_1101x668.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xgul!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ee1a167-b09f-40d1-89aa-a2bd8d7b4034_1101x668.png 424w, /__u/substackcdn.com/image/fetch/$s_!xgul!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ee1a167-b09f-40d1-89aa-a2bd8d7b4034_1101x668.png 848w, /__u/substackcdn.com/image/fetch/$s_!xgul!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ee1a167-b09f-40d1-89aa-a2bd8d7b4034_1101x668.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xgul!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ee1a167-b09f-40d1-89aa-a2bd8d7b4034_1101x668.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Which layer of guardrails do you implement first &#8212; input, processing, or output?<br>&#128172; What's your current latency budget for safety checks in production?<br>&#128172; Have you hit service quota limits with Bedrock Guardrails?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/building-safety-into-genai-what-actually/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/building-safety-into-genai-what-actually/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾AppSync vs API Gateway for GraphQL: The Decision Framework🐾]]></title><description><![CDATA[&#129299;  Previous post about AppSync gathered so many messages and comments regarding the usage of AppSync vs API Gateway for GraphQL, so I decided to write part two. And today I would cover the questions regarding When should you choose API Gateway + Lambda instead? And what&#8217;s the actual cost difference?]]></description><link>https://annpastushko.substack.com/p/appsync-vs-api-gateway-for-graphql</link><guid isPermaLink="false">https://annpastushko.substack.com/p/appsync-vs-api-gateway-for-graphql</guid><pubDate>Mon, 05 Jan 2026 14:02:18 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_23z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbe6a7aa-b284-4084-ab02-fc6a6eb16877_2202x1337.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; Previous post about AppSync gathered so many messages and comments regarding the usage of AppSync vs API Gateway for GraphQL, so I decided to write part two. And today I would cover the questions regarding When should you choose API Gateway + Lambda instead? And what&#8217;s the actual cost difference?</p><h4>&#9989; When API Gateway shines: Heavy business logic</h4><p>If your GraphQL API is primarily business logic rather than data access, API Gateway + Lambda is simpler than AppSync&#8217;s multi-resolver approach.</p><p>Consider an e-commerce pricing engine with discount calculations, tax logic, and external API validations. With API Gateway, you write one Lambda that handles everything:</p><pre><code><code>const resolvers = {
  Query: {
    productPrice: async (_, { productId, userId }) =&gt; {
      const user = await getUserTier(userId);
      const basePrice = await getProductPrice(productId);
      const discount = calculateDiscount(user, basePrice);
      const tax = calculateTax(basePrice, user.location);
      
      return await pricingAPI.validate({ basePrice, discount, tax });
    }
  }
};</code></code></pre><p>With AppSync, you&#8217;d split this into multiple Lambda resolvers plus VTL or APPSYNC_JS orchestration to chain them together. More moving parts, more complexity.</p><p><strong>The cost reality:</strong> For 10M requests/month where each query needs complex processing (500ms total):</p><ul><li><p>AppSync: $40 (Query) + $100 (Lambda) = ~$140 total</p></li><li><p>API Gateway (HTTP API): $10 (Request) + $83.33 (Lambda) = ~$93.33 total</p></li></ul><blockquote><p><em>Note: These are estimates; actual costs depend on memory size and the new 2025 Lambda billing for initialization (cold starts).</em></p></blockquote><p>The pricing model favors consolidated logic over distributed resolvers. So, API Gateway, in most cases, is around 30% cheaper because you avoid AppSync&#8217;s per-query cost and multiple resolver invocations. </p><h4>&#9989; When API Gateway shines: Adding GraphQL to existing REST API</h4><p>Here&#8217;s the migration trap many teams fall into. You have a successful REST API on API Gateway with established auth, monitoring, and deployment pipelines. Now you want to add GraphQL capabilities.</p><p>With AppSync, you&#8217;re building a second, separate API. Different auth configuration (your REST uses Lambda authorizer, now you need Cognito or IAM for AppSync). Different CloudWatch dashboards. Different rate limits and quotas. Different deployment pipelines. Your mobile clients now need to maintain connections to two different APIs with potentially different authentication flows.</p><p>With API Gateway, you add a <em>/graphql</em> route to your existing API. Same Lambda authorizer works for both REST and GraphQL endpoints. Same CloudWatch Logs, same X-Ray traces. Same domain name, same SSL certificate. One unified API surface that your clients interact with.</p><p>The operational overhead of maintaining two separate APIs is real and often underestimated. Double the monitoring dashboards, double the alerting rules, double the cost allocation tracking. If you already have API Gateway infrastructure and tribal knowledge, adding GraphQL there is the path of least resistance.</p><h4>&#9989; When API Gateway shines: High-volume APIs</h4><p>At high request volumes, the per-request pricing difference becomes significant. AppSync charges $4 per million query/mutation requests. API Gateway charges $1 per million requests. This 4x difference compounds at scale.</p><p>For 10M simple queries per month (single DynamoDB lookup, 100ms Lambda execution):</p><ul><li><p> AppSync costs $40 (queries) + $2.50 (DynamoDB) = $42.50</p></li><li><p>API Gateway costs $10 (requests) + $16.67 (Lambda at 128MB, 100ms) + $2.50 (DynamoDB) = $29.17</p></li></ul><p>That&#8217;s 31% cheaper for identical functionality. At 50M requests/month, you&#8217;re saving $67/month. At 100M requests/month, you&#8217;re saving $1,340/month. The math shifts heavily toward API Gateway as volume increases.</p><p><strong>Important caveat:</strong> API Gateway&#8217;s advantage assumes your Lambda execution time stays reasonable. If you&#8217;re doing simple CRUD where AppSync can use direct APPSYNC_JS resolvers (no Lambda), AppSync might win on cost. But in practice, most production APIs need Lambda for business logic anyway, so API Gateway&#8217;s lower base cost tends to win.</p><p>Also worth noting: API Gateway supports 10MB payload sizes versus AppSync&#8217;s 256KB limit. If you&#8217;re handling file uploads, image processing requests, or large batch mutations, AppSync&#8217;s limit becomes a hard constraint. You&#8217;ll need pre-signed S3 URLs as a workaround, while API Gateway handles it natively.</p><h4>&#9989; When API Gateway shines: Local development and debugging</h4><p>Development velocity matters, especially when debugging production issues at 3 AM. API Gateway + Lambda gives you the full Node.js/Python ecosystem and standard debugging workflows.</p><p>You can test your entire GraphQL API locally with <code>sam local start-api</code>, set breakpoints in your IDE, and step through resolver logic. Use any npm package you want&#8212;Winston for logging, Sentry for error tracking, custom middleware for authentication. Your error handling is pure JavaScript:</p><pre><code><code>const server = new ApolloServer({
  formatError: (error) =&gt; {
    logger.error(&#8217;GraphQL error&#8217;, { 
      message: error.message,
      path: error.path,
      user: context.user 
    });
    if (error.message.includes(&#8217;database&#8217;)) {
      return new Error(&#8217;Service temporarily unavailable&#8217;);
    }
    return error;
  }
});</code></code></pre><p>With AppSync, you deploy to AWS to test every VTL or APPSYNC_JS change. No local testing environment. Debugging means parsing verbose JSON logs in CloudWatch and adding <em>$util.log()</em> statements. APPSYNC_JS doesn&#8217;t support npm packages &#8212; you&#8217;re limited to its built-in standard library. X-Ray tracing works, but it feels like an afterthought compared to Lambda&#8217;s first-class X-Ray integration.</p><p>When something breaks in production, API Gateway&#8217;s familiar patterns&#8212;CloudWatch Logs you can grep, X-Ray traces that make sense, error stack traces that point to actual code &#8212; mean faster mean time to recovery. The operational simplicity compounds over time.</p><p><strong>Caching insight:</strong> API Gateway caches the entire GraphQL response per query. AppSync caches per resolver, meaning if your query triggers 5 resolvers, each checks its cache independently. For queries that request multiple fields, API Gateway&#8217;s query-level caching often delivers better cache hit rates than AppSync&#8217;s resolver-level approach.</p><h4>&#9940; The main concern: cold starts</h4><p>The common objection to API Gateway + Lambda is cold start latency. Lambda cold starts for Node.js at 128MB typically add 50-200ms. AppSync&#8217;s APPSYNC_JS resolvers talking directly to DynamoDB have ~5-10ms overhead. Seems like a huge difference, right?</p><p>But here&#8217;s what matters in production: cold starts occur in less than 1% of Lambda invocations according to AWS&#8217;s analysis. Lambda reuses warm execution environments for 10-15 minutes. So 99%+ of your requests hit warm Lambda functions with no cold start penalty.</p><p><strong>If cold starts truly matter for your use case:</strong> Provisioned Concurrency keeps functions warm for $54/month per 5 instances. Lambda SnapStart (Java only) reduces cold starts by 90%. Increasing memory allocation from 128MB to 512MB also speeds up cold starts. These options work identically whether you&#8217;re behind AppSync or API Gateway.</p><h4>&#127873;My decision framework</h4><p><strong>Choose API Gateway + Lambda if:</strong></p><ol><li><p>Adding GraphQL to existing REST API (avoid two separate APIs)</p></li><li><p>Heavy business logic (complex transformations, external APIs, retry logic)</p></li><li><p>High volume (&gt;10M requests/month where per-request cost matters)</p></li><li><p>Team prefers standard Node.js/Python (avoid VTL/APPSYNC_JS learning curve)</p></li><li><p>Need local testing and debugging flexibility</p></li><li><p>Custom auth requirements beyond Cognito/IAM</p></li></ol><p><strong>Choose AppSync if:</strong></p><ol><li><p>Real-time subscriptions are critical (WebSocket lifecycle handled automatically)</p></li><li><p>GraphQL-first greenfield project (no existing REST infrastructure)</p></li><li><p>Simple CRUD with direct data access (DynamoDB/Aurora queries)</p></li><li><p>Low to moderate volume (&lt;5M requests/month)</p></li><li><p>Team wants schema-first discipline enforced</p></li></ol><p><strong>Reality check:</strong> Even IMDb (Amazon-owned) uses API Gateway + Lambda for GraphQL at 10,000+ TPS, not AppSync. It scales.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!_23z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbe6a7aa-b284-4084-ab02-fc6a6eb16877_2202x1337.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!_23z!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbe6a7aa-b284-4084-ab02-fc6a6eb16877_2202x1337.png 424w, /__u/substackcdn.com/image/fetch/$s_!_23z!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbe6a7aa-b284-4084-ab02-fc6a6eb16877_2202x1337.png 848w, /__u/substackcdn.com/image/fetch/$s_!_23z!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbe6a7aa-b284-4084-ab02-fc6a6eb16877_2202x1337.png 1272w, /__u/substackcdn.com/image/fetch/$s_!_23z!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbe6a7aa-b284-4084-ab02-fc6a6eb16877_2202x1337.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!_23z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbe6a7aa-b284-4084-ab02-fc6a6eb16877_2202x1337.png" width="1456" height="884" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cbe6a7aa-b284-4084-ab02-fc6a6eb16877_2202x1337.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:884,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:339878,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/181926634?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbe6a7aa-b284-4084-ab02-fc6a6eb16877_2202x1337.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!_23z!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbe6a7aa-b284-4084-ab02-fc6a6eb16877_2202x1337.png 424w, /__u/substackcdn.com/image/fetch/$s_!_23z!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbe6a7aa-b284-4084-ab02-fc6a6eb16877_2202x1337.png 848w, /__u/substackcdn.com/image/fetch/$s_!_23z!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbe6a7aa-b284-4084-ab02-fc6a6eb16877_2202x1337.png 1272w, /__u/substackcdn.com/image/fetch/$s_!_23z!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcbe6a7aa-b284-4084-ab02-fc6a6eb16877_2202x1337.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Does your team actually use Amplify Mock?<br>&#128172; What&#8217;s the most &#8220;creative&#8221; way you&#8217;ve bypassed the 256KB payload limit?<br>&#128172; If you had to start over today, would you go AppSync?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/appsync-vs-api-gateway-for-graphql/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/appsync-vs-api-gateway-for-graphql/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾AppSync for GraphQL: When it actually shines🐾]]></title><description><![CDATA[&#129299; &#8220;AppSync is for GraphQL&#8221; seems like obvious advice. Yet I keep seeing teams either implement GraphQL on API Gateway + Lambda, or worse &#8212; regret their AppSync choice six months in.

The typical framing is: &#8220;Do you need GraphQL? Use AppSync.&#8221; But the actual question should be: &#8220;What are you optimizing for, and what operational complexity can your team handle?&#8221; Let me show you where AppSync genuinely excels &#8212; and the hidden costs.]]></description><link>https://annpastushko.substack.com/p/appsync-for-graphql-when-it-actually</link><guid isPermaLink="false">https://annpastushko.substack.com/p/appsync-for-graphql-when-it-actually</guid><pubDate>Mon, 22 Dec 2025 14:01:27 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!1S9V!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a6b3e8-4e49-47c3-b56a-bf7fcb179cc0_2202x1337.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; &#8220;AppSync is for GraphQL&#8221; seems like obvious advice. Yet I keep seeing teams either implement GraphQL on API Gateway + Lambda, or worse &#8212; regret their AppSync choice six months in.</p><p>The typical framing is: &#8220;Do you need GraphQL? Use AppSync.&#8221; But the actual question should be: &#8220;What are you optimizing for, and what operational complexity can your team handle?&#8221; Let me show you where AppSync genuinely excels &#8212; and the hidden costs.</p><h4>&#9989; When AppSync shines: Real-time subscriptions</h4><p>AppSync subscriptions are transformative for real-time updates. They handle WebSocket connections, message filtering, and fan-out automatically.</p><pre><code><code>type Subscription {
  onOrderStatusChange(customerId: ID!): Order
    @aws_subscribe(mutations: [&#8221;updateOrderStatus&#8221;])
}</code></code></pre><p>With API Gateway, you&#8217;d build this yourself: WebSocket APIs + DynamoDB connection tracking + Lambda broadcasting. That&#8217;s 3-4 services vs AppSync&#8217;s built-in handling.</p><p><strong>The cost reality:</strong> WebSocket connections cost money even when idle.</p><p>For 10,000 users connected 8 hours/day (rough estimate):</p><ul><li><p>Connections: $0.08/million minutes = ~$384/day</p></li><li><p>Messages: $1.00/million messages</p></li></ul><p>If your &#8220;real-time&#8221; is actually &#8220;poll every 30 seconds,&#8221; REST might be cheaper.</p><h4>&#9989; When AppSync shines: Multi-source data federation</h4><p>One GraphQL query, multiple data sources in parallel:</p><pre><code><code>type User {
  profile: UserProfile      # DynamoDB
  orders: [Order]           # RDS Aurora
  recommendations: [Product] # Lambda + ML
}</code></code></pre><p>AppSync orchestrates this automatically. With API Gateway + Lambda, you write the orchestration logic yourself.</p><p><strong>The N+1 problem:</strong> Watch out for this query:</p><pre><code><code>{
  users(limit: 100) {
    orders  # 100 separate DB calls!
  }
}</code></code></pre><p>AppSync makes 1 call for users, then 100 calls for orders. You need DataLoader patterns or batch resolvers to fix this.</p><h4>&#9989; When AppSync shines: Schema-first discipline</h4><p>AppSync forces you to maintain a <code>.graphql</code> schema file as your API contract:</p><ul><li><p>Frontend teams work from schema immediately</p></li><li><p>Breaking changes are explicit</p></li><li><p>Auto-generated documentation via GraphiQL</p></li><li><p>Prevents &#8220;nobody knows what the API actually does&#8221; syndrome</p></li></ul><p>This is less about AppSync features and more about enforced discipline that pays off.</p><h4>&#9989; When AppSync shines: Field-level authorization</h4><p>AppSync&#8217;s authorization is underrated. You get four modes (API Key, IAM, Cognito, OIDC) that can be applied per-field:</p><pre><code>type User {
  id: ID!
  email: String @aws_auth(cognito_groups: [&#8221;Users&#8221;])
  salary: Float @aws_auth(cognito_groups: [&#8221;Admins&#8221;])
}</code></pre><p>Same type, different field-level permissions. With API Gateway, you&#8217;d implement this in every Lambda resolver manually. AppSync supports multiple auth modes per API natively. Some fields use Cognito, others use IAM, others use API keys &#8212; all in one schema.</p><p><strong>The limitation: </strong>Authorization is tied to Cognito groups or IAM policies. Custom RBAC (permissions stored in your DB) still needs Lambda resolver logic.</p><h4>&#9940; When AppSync disappoints: The VTL reality</h4><p>AppSync promises &#8220;no Lambda needed&#8221; via VTL (Velocity Template Language) resolvers:</p><pre><code><code>{
  &#8220;version&#8221;: &#8220;2017-02-28&#8221;,
  &#8220;operation&#8221;: &#8220;Query&#8221;,
  &#8220;query&#8221;: {
    &#8220;expression&#8221;: &#8220;PK = :pk&#8221;,
    &#8220;expressionValues&#8221;: {
      &#8220;:pk&#8221;: $util.dynamodb.toDynamoDBJson($ctx.args.userId)
    }
  }
}</code></code></pre><p>This works for simple CRUD. But add:</p><ul><li><p>Business logic (discounts, access control)</p></li><li><p>External API calls with retries</p></li><li><p>Complex data transformations</p></li><li><p>Any <code>if/else</code> beyond basics</p></li></ul><p>You&#8217;re writing Lambda anyway. Now you maintain VTL templates AND Lambda code.</p><p><strong>Reality check:</strong> In every production AppSync API I&#8217;ve observed, 60%+ of resolvers use Lambda. The &#8220;no Lambda&#8221; promise rarely survives contact with real requirements.</p><p><strong>Update (late 2022):</strong> AWS introduced APPSYNC_JS runtime as a VTL replacement. You can now write resolvers in JavaScript. It&#8217;s still not full Node.js. No npm packages, limited standard library. Async/await works for AppSync operations, but external HTTP calls need Lambda anyway. For complex business logic, you&#8217;re still reaching for Lambda. But for data access patterns, APPSYNC_JS is a significant improvement over VTL.</p><h4>&#127873;Production gotchas:</h4><ul><li><p><strong>Pipeline resolvers</strong>: use these to chain multiple logic steps (e.g., Auth &#8594; Validation &#8594; DB call) without repeating code. They turn fragmented VTL/JS into a reusable sequence. Powerful but under-documented.</p></li><li><p><strong>HTTP resolvers</strong>: allow AppSync to act as a <strong>GraphQL Gateway</strong>. You can proxy existing REST or even external GraphQL APIs directly, merging legacy services into your new graph.</p></li><li><p><strong>Caching</strong>: AppSync now offers two layers. Resolver-level caching is the default (5 resolvers check cache 5 times), but Operation-level caching (new in 2025) finally allows you to cache the entire query response natively.</p></li><li><p><strong>256KB request limit</strong>: a hard limit for the total request (payload + headers). While API Gateway handles 10MB, AppSync requires you to offload large data or file uploads to S3 using pre-signed URLs.</p></li><li><p><strong>VTL has no debugger</strong>: Unlike API Gateway, there is no true local runtime. You can use basic simulators like Amplify Mock, but for complex logic, you are stuck with <em>$util.log() </em>and CloudWatch. Local testing remains the biggest developer experience bottleneck.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!1S9V!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a6b3e8-4e49-47c3-b56a-bf7fcb179cc0_2202x1337.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!1S9V!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a6b3e8-4e49-47c3-b56a-bf7fcb179cc0_2202x1337.png 424w, /__u/substackcdn.com/image/fetch/$s_!1S9V!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a6b3e8-4e49-47c3-b56a-bf7fcb179cc0_2202x1337.png 848w, /__u/substackcdn.com/image/fetch/$s_!1S9V!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a6b3e8-4e49-47c3-b56a-bf7fcb179cc0_2202x1337.png 1272w, /__u/substackcdn.com/image/fetch/$s_!1S9V!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a6b3e8-4e49-47c3-b56a-bf7fcb179cc0_2202x1337.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!1S9V!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a6b3e8-4e49-47c3-b56a-bf7fcb179cc0_2202x1337.png" width="1456" height="884" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/38a6b3e8-4e49-47c3-b56a-bf7fcb179cc0_2202x1337.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:884,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:662985,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/181918596?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a6b3e8-4e49-47c3-b56a-bf7fcb179cc0_2202x1337.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!1S9V!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a6b3e8-4e49-47c3-b56a-bf7fcb179cc0_2202x1337.png 424w, /__u/substackcdn.com/image/fetch/$s_!1S9V!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a6b3e8-4e49-47c3-b56a-bf7fcb179cc0_2202x1337.png 848w, /__u/substackcdn.com/image/fetch/$s_!1S9V!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a6b3e8-4e49-47c3-b56a-bf7fcb179cc0_2202x1337.png 1272w, /__u/substackcdn.com/image/fetch/$s_!1S9V!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38a6b3e8-4e49-47c3-b56a-bf7fcb179cc0_2202x1337.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Have you tried AppSync in production?<br>&#128172; Do you use APPSYNC_JS or still stuck with VTL?<br>&#128172; What&#8217;s your biggest pain point with GraphQL?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/appsync-for-graphql-when-it-actually/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/appsync-for-graphql-when-it-actually/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾Lambda is a worker, not a Boss🐾]]></title><description><![CDATA[&#129299; Does your Lambda function call Service A, check the response, retry with delays if it fails, then call Service B, while writing the current state to DynamoDB after each step?]]></description><link>https://annpastushko.substack.com/p/lambda-is-a-worker-not-a-boss</link><guid isPermaLink="false">https://annpastushko.substack.com/p/lambda-is-a-worker-not-a-boss</guid><pubDate>Mon, 08 Dec 2025 14:03:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!wIrB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ce1a101-5837-46aa-99fe-3236f4d95bdf_2202x1337.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; Does your Lambda function call Service A, check the response, retry with delays if it fails, then call Service B, while writing the current state to DynamoDB after each step? Here&#8217;s what&#8217;s actually happening: you&#8217;ve built an orchestrator inside your Lambda. It looks simple at first &#8212; one function, one file, easy to understand. But you&#8217;re managing workflow state manually, coding retry logic from scratch, and paying for compute time during every error handling routine. There&#8217;s a better way.</p><h4>The Trap: The Lambda orchestration anti-pattern</h4><p>Developers frequently fall into this trap because writing sequential logic in code is fast and familiar. However, this approach turns your business flow into fragile <strong>&#8220;spaghetti code&#8221;</strong> that is impossible to maintain and costly to run.</p><p>The three primary failure points of using Lambda for orchestration:</p><ul><li><p><strong>The 15-Minute Timeout Wall:</strong> Your Lambda execution is capped at 15 minutes. If your workflow involves waiting for an external API, a human approval, or a long-running process, the function will simply time out and fail the entire process. No graceful pause. No resume. Just failure.</p></li><li><p><strong>Manual State Management:</strong> Lambda is stateless by design. To coordinate steps, you are forced to write and maintain complex custom logic that updates external state (e.g., DynamoDB) after every step. You&#8217;re essentially building your own orchestration engine from scratch &#8212; poorly.</p></li><li><p><strong>Expensive Retries:</strong> You must code error handling and retry logic into your function. When a service fails, you pay for your code to run the error logic, instead of having the orchestration layer handle it almost for free. Worse, if your Lambda times out mid-retry, you&#8217;ve lost all context about where you were in the process.</p></li></ul><h4>The Architect&#8217;s choice: Step Functions</h4><p>AWS Step Functions is the <strong>dedicated, stateful orchestrator</strong>. It solves all of the above problems by defining your workflow declaratively using the Amazon States Language (ASL).</p><ul><li><p><strong>Year-Long Workflows:</strong> Standard workflows can run for up to one year. You pay for the compute (Lambda) only when a task is executing. Waiting for days or weeks for human approval is cost-effective, unlike an idle Lambda that would timeout long before anyone even opens the approval request.</p></li><li><p><strong>Built-in Resilience:</strong> Error retries, exponential backoff, complex branching, and fallbacks are defined visually in the state machine. This logic is robust, versionable, and you don&#8217;t write a single line of code for it. When a downstream service fails, Step Functions automatically retries with configurable intervals &#8212; no billable Lambda invocations during the wait.</p></li><li><p><strong>Clear Observability:</strong> The visual workflow console shows you the precise status of every step in a clear graph, making debugging a failure instantly obvious. Instead of searching through scattered CloudWatch logs trying to reconstruct what happened, you see exactly which state failed, what input it received, and what error it threw.</p></li></ul><h4>The Solution: What to do about it</h4><ol><li><p><strong>Map your current workflows:</strong> Identify Lambda functions that contain more than one business logic step. These are your refactoring candidates.</p></li><li><p><strong>Start with the long-running ones:</strong> If you have any workflow that needs to wait more than 5 minutes for anything&#8212;an API, a human, a batch job&#8212;move it to Step Functions immediately.</p></li><li><p><strong>Use the .waitForTaskToken pattern:</strong> For human approvals or external dependencies, leverage Step Functions&#8217; callback pattern. Your workflow pauses indefinitely (up to one year) without any compute cost, then resumes exactly where it left off when the external event completes.</p></li><li><p><strong>Let Step Functions handle the boring parts:</strong> Error handling, retries, timeouts, and branching logic should live in your state machine definition, not scattered across Lambda code.</p></li></ol><p>Lambda is exceptional at executing discrete tasks. Step Functions is exceptional at coordinating them. Use each for what it does best, and your workflows become more reliable, maintainable, and cheaper to run.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!wIrB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ce1a101-5837-46aa-99fe-3236f4d95bdf_2202x1337.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!wIrB!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ce1a101-5837-46aa-99fe-3236f4d95bdf_2202x1337.png 424w, /__u/substackcdn.com/image/fetch/$s_!wIrB!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ce1a101-5837-46aa-99fe-3236f4d95bdf_2202x1337.png 848w, /__u/substackcdn.com/image/fetch/$s_!wIrB!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ce1a101-5837-46aa-99fe-3236f4d95bdf_2202x1337.png 1272w, /__u/substackcdn.com/image/fetch/$s_!wIrB!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ce1a101-5837-46aa-99fe-3236f4d95bdf_2202x1337.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!wIrB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ce1a101-5837-46aa-99fe-3236f4d95bdf_2202x1337.png" width="1456" height="884" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0ce1a101-5837-46aa-99fe-3236f4d95bdf_2202x1337.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:884,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:482515,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/180535319?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ce1a101-5837-46aa-99fe-3236f4d95bdf_2202x1337.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!wIrB!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ce1a101-5837-46aa-99fe-3236f4d95bdf_2202x1337.png 424w, /__u/substackcdn.com/image/fetch/$s_!wIrB!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ce1a101-5837-46aa-99fe-3236f4d95bdf_2202x1337.png 848w, /__u/substackcdn.com/image/fetch/$s_!wIrB!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ce1a101-5837-46aa-99fe-3236f4d95bdf_2202x1337.png 1272w, /__u/substackcdn.com/image/fetch/$s_!wIrB!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ce1a101-5837-46aa-99fe-3236f4d95bdf_2202x1337.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; What&#8217;s the longest workflow you&#8217;re currently running in a single Lambda function?<br>&#128172; Have you ever lost workflow state due to a Lambda timeout?<br>&#128172; What&#8217;s stopping you from adopting Step Functions for orchestration?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/lambda-is-a-worker-not-a-boss/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/lambda-is-a-worker-not-a-boss/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾Step Functions for human-in-the-loop MLOps🐾]]></title><description><![CDATA[&#129299;You can automate almost every part of an ML workflow &#8212; except the human. Approvals are essential because few teams are comfortable pushing a model to production without someone reviewing the results first. The problem is that waiting for that approval can get expensive if you&#8217;re running resources or polling for state changes. Here&#8217;s how to pause a serverless workflow for as long as needed without paying for idle compute.]]></description><link>https://annpastushko.substack.com/p/step-functions-for-human-in-the-loop</link><guid isPermaLink="false">https://annpastushko.substack.com/p/step-functions-for-human-in-the-loop</guid><pubDate>Mon, 24 Nov 2025 14:02:37 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Ksg_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf00bd06-7a0e-43b6-898a-713fe5310e1e_2202x1337.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299;You can automate almost every part of an ML workflow &#8212; except the human. Approvals are essential because few teams are comfortable pushing a model to production without someone reviewing the results first. The problem is that waiting for that approval can get expensive if you&#8217;re running resources or polling for state changes. Here&#8217;s how to pause a serverless workflow for as long as needed without paying for idle compute.</p><h4>The core problem: waiting is expensive</h4><p>In a modern MLOps pipeline, we often reach a critical point&#8212;such as before deploying a model to production, or tagging a batch of data&#8212;where a human expert must review the outcome.</p><p>In a serverless environment, implementing this manual gate traditionally presents a dilemma:</p><ol><li><p><strong>The Polling anti-pattern:</strong> Use a Lambda function that polls a database (e.g., DynamoDB) every minute until the human updates the status. This is inefficient, complex, and burns unnecessary compute costs.</p></li><li><p><strong>The Container anti-pattern:</strong> Keep a container (EC2 or Fargate) running 24/7 waiting for a state change. This defeats the purpose of serverless and leads to high, fixed costs.</p></li></ol><p>The solution must be both durable (it can wait days or weeks without failing) and cost-effective (we don&#8217;t pay for idle compute).</p><h4>The Step Functions solution: callback task</h4><p>AWS Step Functions is the ideal orchestration tool for this, specifically through its <strong>Callback Task</strong> pattern using the <em>.waitForTaskToken</em> integration.</p><p>This pattern allows the State Machine to initiate an action (like sending a notification to a human), pause its execution, and wait indefinitely (up to one year) for a unique <em>Task Token</em> to be returned.</p><p>Process flow:</p><ol><li><p><strong>Trigger:</strong> The Step Function is initiated (e.g., by an S3 upload, scheduled Lambda, or commit event).</p></li><li><p><strong>Pause:</strong> The State Machine reaches an AWS service integration configured with <em>.waitForTaskToken</em>. This service could be:</p><ul><li><p><strong>SNS/SQS:</strong> Send a message to a human-facing queue or topic.</p></li><li><p><strong>Lambda:</strong> Invoke a Lambda that creates a message with the <em>Task Token</em>.</p></li></ul></li><li><p><strong>Human Action:</strong> The unique <em>Task Token</em> is passed to your external interface (e.g., a simple Slack message). The human reviews the data or model output.</p></li><li><p><strong>Resume:</strong> When the human hits &#8220;Approve&#8221; or &#8220;Reject,&#8221; your external service calls the Step Functions API using one of two methods, including the original <em>Task Token</em>:</p><ul><li><p><em>SendTaskSuccess</em>: Workflow proceeds to the next step.</p></li><li><p><em>SendTaskFailure</em>: Workflow terminates or enters an error handling state.</p></li></ul></li></ol><p>During the entire wait time, you are only charged for the State Transition (the start of the waiting state) and the duration of the wait in seconds, which is significantly cheaper than hourly compute.</p><h4>Implementation Details</h4><p>In your Amazon States Language (ASL) definition, you can add it with the following configuration:</p><pre><code> &#8220;WaitApproval&#8221; : {
        &#8220;Type&#8221;     : &#8220;Task&#8221;,
        &#8220;Resource&#8221; : &#8220;arn:aws:states:::lambda:invoke.waitForTaskToken&#8221;
        &#8220;Parameters&#8221; : {
           &#8220;Parameter_1&#8221;: "value",
           &#8220;Payload&#8221;: {
              &#8220;token.$&#8221;: &#8220;$$.Task.Token&#8221;
     }
   }
 }</code></pre><p>By leveraging the <em>Task Token</em>, you can get a durable, auditable, and inherently cost-optimized serverless workflow component for the MLOps process. A full example of such an approval step can be found in the <a href="https://github.com/ChildishGirl/mlops-serverless-pipeline/tree/main">MLOps Serverless pipeline</a> project.</p><h4>&#127873;Add SLA to your approval flow</h4><p>Add a <em>TimeoutSeconds</em> parameter directly to your callback task. When a timeout occurs, Step Functions throws a <em>States.Timeout</em> error. Use a <em>Catch</em> block on that error to redirect to an escalation state &#8212; send SNS to backup reviewers, or trigger a Lambda that sends a reminder or escalates to a team leader.</p><p>This ensures stalled approvals don&#8217;t become black holes. The timeout becomes your SLA enforcement mechanism, automatically escalating when humans don&#8217;t respond within your defined window. It also reduces charges for the State Transition.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Ksg_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf00bd06-7a0e-43b6-898a-713fe5310e1e_2202x1337.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Ksg_!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf00bd06-7a0e-43b6-898a-713fe5310e1e_2202x1337.png 424w, /__u/substackcdn.com/image/fetch/$s_!Ksg_!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf00bd06-7a0e-43b6-898a-713fe5310e1e_2202x1337.png 848w, /__u/substackcdn.com/image/fetch/$s_!Ksg_!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf00bd06-7a0e-43b6-898a-713fe5310e1e_2202x1337.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Ksg_!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf00bd06-7a0e-43b6-898a-713fe5310e1e_2202x1337.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Ksg_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf00bd06-7a0e-43b6-898a-713fe5310e1e_2202x1337.png" width="1456" height="884" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/df00bd06-7a0e-43b6-898a-713fe5310e1e_2202x1337.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:884,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:228313,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/179458949?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf00bd06-7a0e-43b6-898a-713fe5310e1e_2202x1337.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!Ksg_!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf00bd06-7a0e-43b6-898a-713fe5310e1e_2202x1337.png 424w, /__u/substackcdn.com/image/fetch/$s_!Ksg_!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf00bd06-7a0e-43b6-898a-713fe5310e1e_2202x1337.png 848w, /__u/substackcdn.com/image/fetch/$s_!Ksg_!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf00bd06-7a0e-43b6-898a-713fe5310e1e_2202x1337.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Ksg_!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdf00bd06-7a0e-43b6-898a-713fe5310e1e_2202x1337.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Beyond Step Functions, what is your preferred orchestrator for MLOps pipelines, and why?<br>&#128172; What is your solution for implementing an approval step in the pipeline? <br>&#128172; What is your best tip for creating a reliable MLOps pipeline?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/step-functions-for-human-in-the-loop/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/step-functions-for-human-in-the-loop/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xINW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xINW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/160495620?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xINW!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F72715d29-0c2d-4a7a-bdf9-437dd82f7233_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month &#8212; I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾Lessons learned from recent AWS outage🐾]]></title><description><![CDATA[&#129299;During the October 19-20 us-east-1 incident, thousands of Multi-AZ deployments stayed perfectly healthy &#8212; while experiencing intermittent scaling issues. Zero data loss. Zero downtime for running workloads. But reduced ability to launch new resources during a cascading failure that took 14 hours to fully resolve.]]></description><link>https://annpastushko.substack.com/p/lessons-learned-from-recent-aws-outage</link><guid isPermaLink="false">https://annpastushko.substack.com/p/lessons-learned-from-recent-aws-outage</guid><pubDate>Mon, 10 Nov 2025 14:03:28 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Yevq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc24b623-3831-4fb7-8718-af08859e05e0_2202x1337.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; During the October 19-20 us-east-1 incident, thousands of Multi-AZ deployments stayed perfectly healthy &#8212; while experiencing intermittent scaling issues. Zero data loss. Zero downtime for running workloads. But reduced ability to launch new resources during a cascading failure that took 14 hours to fully resolve.</p><p><strong>Important context</strong>: AWS&#8217;s <a href="https://aws.amazon.com/message/101925/">post-incident report</a> reveals this was caused by a race condition in DynamoDB&#8217;s DNS management system and cascading operational failures&#8212;bugs AWS is now fixing, not inherent architectural limitations. However, the incident still teaches us important lessons about resilience, dependencies, and matching architecture to business requirements.</p><p>This incident prompted many teams to reassess their resilience strategies. Some realized their Multi-Region setup was overkill for their actual requirements. Others discovered gaps in their ability to handle regional issues. Where does your architecture fall?</p><h3>Lesson 1: Multi-AZ protects your data plane, not your control plane</h3><p>he incident demonstrated an important operational principle: while data planes can be multi-AZ, control planes are typically regional. During the incident, this meant:</p><ul><li><p>Running EC2 instances stayed healthy (data plane)</p></li><li><p>New EC2 launches experienced failures (control plane)</p></li><li><p>Existing containers kept running (data plane)</p></li><li><p>New deployments faced intermittent issues (control plane)</p></li></ul><p>This particular incident was caused by bugs &#8212; a race condition and cascading failures that AWS is fixing. But it reveals a broader reality: when regional services fail (whether due to bugs, misconfigurations, capacity issues, or other operational problems), Multi-AZ won&#8217;t help you scale.</p><p><strong>What to do about it:</strong></p><ul><li><p><strong>Map your scaling dependencies: </strong>List every component that launches new resources during scaling&#8212;ASGs, ECS services, Lambda cold starts. These are your blind spots.</p></li><li><p><strong>Document your RTO honestly:</strong> If your RTO assumes you can scale during an incident, Multi-AZ won&#8217;t meet it. Write this down explicitly.</p></li><li><p><strong>Run a &#8220;no-launch&#8221; game day:</strong> Simulate control plane unavailability. Can your existing capacity survive peak traffic?</p></li><li><p><strong>Apply WAF REL10-BP04: </strong>For workloads with RTO &lt; 4 hours, implement Multi-Region fault isolation.</p></li></ul><h3>Lesson 2: Match your architecture to your actual RTO requirements</h3><p>Multi-AZ is designed for high availability within a region&#8212;and it works exceptionally well for that purpose. During this incident, Multi-AZ deployments kept existing workloads running without data loss.</p><p>The challenge isn&#8217;t that Multi-AZ is insufficient &#8212; it&#8217;s understanding when it&#8217;s enough. Multi-AZ protects against AZ failures and keeps running infrastructure healthy. When regional issues occur (whether architectural limitations or operational failures), you need different strategies.</p><p><strong>What to do about it:</strong></p><ul><li><p><strong>Define real business impact: </strong>Ask stakeholders: &#8220;What does 6 hours of downtime actually cost us?&#8221; This drives your architecture, not preferences.</p></li><li><p><strong>Calculate Multi-Region costs: </strong>Multiply your AWS bill by 2.5x. Can the business justify that for rare regional failures?</p></li><li><p><strong>Choose your strategy based on requirements:</strong></p><ul><li><p>RTO &gt; 4 hours: Multi-AZ + 20-30% excess capacity</p></li><li><p>RTO 1-4 hours: Multi-AZ + warm standby</p></li><li><p>RTO &lt; 1 hour: Multi-Region active-active</p></li></ul></li><li><p><strong>Over-provision capacity:</strong> If staying single-region, run 20-30% excess capacity so you don&#8217;t need to scale during incidents. This only works if your existing instances stay healthy and your traffic stays predictable.</p></li></ul><h3>Lesson 3: Practice Multi-Region failover before you need it</h3><p>Global Tables performed well during this incident: customers with replicas in healthy regions could continue reading and writing data. Most replicas caught up within 2.5 hours of the incident start.</p><p>With asynchronous replication, there&#8217;s always some replication lag during incidents (WAF REL09-BP02). This isn&#8217;t a limitation unique to Global Tables&#8212;it&#8217;s fundamental to distributed systems. Synchronous replication across regions would add significant latency to every write operation.</p><p>For most applications, accepting temporary divergence during rare regional incidents is a reasonable trade-off for the performance and availability benefits. But having Multi-Region infrastructure deployed doesn&#8217;t mean you&#8217;re ready to use it.</p><p><strong>What to do about it:</strong></p><ul><li><p><strong>Practice failover quarterly:</strong> Actually fail over to your secondary region and time it. Don&#8217;t discover issues during a real incident.</p></li><li><p><strong>Handle conflicts in your app logic:</strong> Identify tables with potential write conflicts and implement application-level resolution.</p></li><li><p><strong>Configure Route 53 failover properly:</strong> Use health checks that test actual endpoints, not just instance health (WAF REL13-BP01).</p></li><li><p><strong>Test your runbook:</strong> If you can&#8217;t execute failover in under 30 minutes following your documented steps, simplify the process.</p></li><li><p><strong>Monitor replication lag:</strong> Set CloudWatch alarms for lag &gt; 5 minutes. Catch problems early.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Yevq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc24b623-3831-4fb7-8718-af08859e05e0_2202x1337.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Yevq!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc24b623-3831-4fb7-8718-af08859e05e0_2202x1337.png 424w, /__u/substackcdn.com/image/fetch/$s_!Yevq!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc24b623-3831-4fb7-8718-af08859e05e0_2202x1337.png 848w, /__u/substackcdn.com/image/fetch/$s_!Yevq!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc24b623-3831-4fb7-8718-af08859e05e0_2202x1337.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Yevq!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc24b623-3831-4fb7-8718-af08859e05e0_2202x1337.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Yevq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc24b623-3831-4fb7-8718-af08859e05e0_2202x1337.png" width="1456" height="884" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cc24b623-3831-4fb7-8718-af08859e05e0_2202x1337.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:884,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:484202,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/178282205?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc24b623-3831-4fb7-8718-af08859e05e0_2202x1337.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!Yevq!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc24b623-3831-4fb7-8718-af08859e05e0_2202x1337.png 424w, /__u/substackcdn.com/image/fetch/$s_!Yevq!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc24b623-3831-4fb7-8718-af08859e05e0_2202x1337.png 848w, /__u/substackcdn.com/image/fetch/$s_!Yevq!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc24b623-3831-4fb7-8718-af08859e05e0_2202x1337.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Yevq!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc24b623-3831-4fb7-8718-af08859e05e0_2202x1337.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Which of your mission-critical systems is still running Multi-AZ only?<br>&#128172; What&#8217;s your current RTO for your core database?<br>&#128172; Have you ever had a regional control plane failure?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/lessons-learned-from-recent-aws-outage/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/lessons-learned-from-recent-aws-outage/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!SWmU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb996ad98-8cc0-492e-8a0d-c002c0ebe43a_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!SWmU!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb996ad98-8cc0-492e-8a0d-c002c0ebe43a_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!SWmU!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb996ad98-8cc0-492e-8a0d-c002c0ebe43a_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!SWmU!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb996ad98-8cc0-492e-8a0d-c002c0ebe43a_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!SWmU!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb996ad98-8cc0-492e-8a0d-c002c0ebe43a_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!SWmU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb996ad98-8cc0-492e-8a0d-c002c0ebe43a_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b996ad98-8cc0-492e-8a0d-c002c0ebe43a_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/178282205?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb996ad98-8cc0-492e-8a0d-c002c0ebe43a_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!SWmU!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb996ad98-8cc0-492e-8a0d-c002c0ebe43a_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!SWmU!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb996ad98-8cc0-492e-8a0d-c002c0ebe43a_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!SWmU!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb996ad98-8cc0-492e-8a0d-c002c0ebe43a_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!SWmU!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb996ad98-8cc0-492e-8a0d-c002c0ebe43a_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month - I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[🐾Fine tuning myths that kill your RDS performance🐾]]></title><description><![CDATA[&#129299; When you migrate your data to a managed service like AWS RDS (especially PostgreSQL), the last thing you expect is to be debugging performance issues caused by a bad tip you read online.]]></description><link>https://annpastushko.substack.com/p/fine-tuning-myths-killing-your-rds</link><guid isPermaLink="false">https://annpastushko.substack.com/p/fine-tuning-myths-killing-your-rds</guid><pubDate>Mon, 27 Oct 2025 14:03:09 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/c698f3ad-9352-4524-a142-0eb087a58c3a_2202x1337.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#129299; When you migrate your data to a managed service like AWS RDS (especially PostgreSQL), the last thing you expect is to be debugging performance issues caused by a bad tip you read online. </p><p>What makes RDS different? Unlike on-premise setups, you can&#8217;t tune OS-level parameters, adjust I/O schedulers, or control storage directly. RDS instances use EBS-backed network storage, where performance depends on instance type, EBS bandwidth allocation, and OS page cache behavior. This fundamentally changes how you should approach performance tuning &#8212; many traditional rules of thumb simply don&#8217;t apply.</p><h4>Myth 1: Keep the default value for <code>work_mem</code> </h4><p>The default 4MB for working memory is a legacy minimum, not an optimal configuration for production.</p><p><strong>The Problem: </strong>When a query operation (like a sort or hash) exceeds 4MB, PostgreSQL performs an expensive <strong>disk spill</strong>, writing temporary files to storage. This I/O bottleneck drastically slows queries. On a modern server with ample RAM, this is pure resource underutilization.</p><p><strong>The Fix: Tune for memory</strong></p><ul><li><p><strong>Increase </strong><code>work_mem</code> to a moderate, production-appropriate value (e.g., 16MB&#8722;64MB) via a custom DB Parameter Group.</p></li><li><p><strong>Monitor spills:</strong> Set <code>log_temp_files</code> to a low value (e.g., 1MB) to identify and correct the specific queries causing I/O bottlenecks.</p></li><li><p><strong>Balance carefully</strong>: Higher <code>work_mem</code> improves performance, but multiply it by your connection count to ensure you don&#8217;t exceed available RAM.</p></li><li><p>&#9888;&#65039;Critical caveat: <code>work_mem</code> is per-operation, per-connection. A query with 3 hash joins on one connection uses 3&#215; <code>work_mem</code>. With 100 connections averaging 2 operations each, <code>work_mem=64MB</code> could consume 12.8GB RAM. Formula: work_mem &#215; operations &#215; connections.</p></li></ul><h4>Myth 2: Push all logic into functions and triggers</h4><p>Advocating for a &#8220;fat database&#8221; architecture creates immediate scaling and maintenance challenges in cloud environments.</p><p><strong>The Problem: </strong>Placing core business logic (e.g., order processing) in the database layer creates a <strong>monolithic bottleneck</strong>. This sacrifices the horizontal scaling capabilities of your application servers and impedes modern CI/CD pipelines, making versioning and testing complex.</p><p><strong>The Fix: Separation of concerns</strong></p><ul><li><p><strong>Business logic:</strong> Belongs entirely in the application layer for easier unit testing and faster iteration.</p></li><li><p><strong>Database logic:</strong> Should be strictly limited to maintaining data integrity (Foreign Keys, <code>CHECK</code> constraints, and simple, non-blocking auditing via triggers).</p></li></ul><h4>Myth 3: Always use <code>UNION</code> vs. <code>UNION ALL</code> </h4><p>The belief that <code>UNION</code> is the standard relational operator ignores its significant performance overhead.</p><p><strong>The Problem: </strong>The <code>UNION</code> operator performs an implicit <strong>deduplication step</strong> (typically using hash aggregation) to ensure all rows in the combined result set are unique. While this doesn&#8217;t always involve sorting, it still adds significant computational overhead. The UNION ALL operator simply concatenates the two result sets <strong>without deduplication overhead</strong>.</p><p><strong>The Fix: Prioritize performance</strong></p><ul><li><p>Prefer<strong> </strong><code>UNION ALL</code> unless the explicit removal of duplicate rows is a business requirement.</p></li><li><p>The performance difference, especially on large result sets or within recursive queries, can be orders of magnitude.</p></li></ul><h4>Myth 4: Trust default <code>autovacuum</code> settings </h4><p>Relying on AWS defaults for <code>autovacuum</code> is insufficient for databases with high write/transaction volume.</p><p><strong>The Problem:</strong> The conservative default thresholds often allow <strong>table and index bloat</strong> to accumulate. This degrades query performance. More critically, they increase the risk of <strong>Transaction ID (XID) wraparound</strong>, which forces an emergency vacuum that can stall the entire database instance.</p><p><strong>The Fix: Tune for velocity</strong></p><ul><li><p>Decrease the <code>autovacuum_vacuum_scale_factor</code> (e.g., from 0.2 to 0.05) on high-write tables to trigger vacuuming sooner.</p></li><li><p>Significantly increase<strong> </strong><code>autovacuum_work_mem</code> (e.g., 128MB or more) to enable vacuum workers to process indexes in memory, preventing bloat efficiently.</p></li><li><p>Consider increasing <code>autovacuum_max_workers</code> (default is 3 on RDS) for larger databases with many active tables &#8212; this allows parallel vacuum operations across multiple tables. However, this setting is capped by available CPU cores &#8212; on smaller instances, increasing workers won&#8217;t help and may cause contention. </p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!9P1G!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fbeb127-e777-4ae7-b6f5-c491f1fc4375_2202x1337.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!9P1G!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fbeb127-e777-4ae7-b6f5-c491f1fc4375_2202x1337.png 424w, /__u/substackcdn.com/image/fetch/$s_!9P1G!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fbeb127-e777-4ae7-b6f5-c491f1fc4375_2202x1337.png 848w, /__u/substackcdn.com/image/fetch/$s_!9P1G!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fbeb127-e777-4ae7-b6f5-c491f1fc4375_2202x1337.png 1272w, /__u/substackcdn.com/image/fetch/$s_!9P1G!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fbeb127-e777-4ae7-b6f5-c491f1fc4375_2202x1337.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!9P1G!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fbeb127-e777-4ae7-b6f5-c491f1fc4375_2202x1337.png" width="1456" height="884" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8fbeb127-e777-4ae7-b6f5-c491f1fc4375_2202x1337.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:884,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:306539,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/153973517?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fbeb127-e777-4ae7-b6f5-c491f1fc4375_2202x1337.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!9P1G!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fbeb127-e777-4ae7-b6f5-c491f1fc4375_2202x1337.png 424w, /__u/substackcdn.com/image/fetch/$s_!9P1G!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fbeb127-e777-4ae7-b6f5-c491f1fc4375_2202x1337.png 848w, /__u/substackcdn.com/image/fetch/$s_!9P1G!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fbeb127-e777-4ae7-b6f5-c491f1fc4375_2202x1337.png 1272w, /__u/substackcdn.com/image/fetch/$s_!9P1G!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8fbeb127-e777-4ae7-b6f5-c491f1fc4375_2202x1337.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h4>Thank you for reading, let&#8217;s chat &#128172;</h4><p>&#128172; Do you still use any triggers or stored functions in your application logic? If so, what specific use case justified the trade-off?<br>&#128172; Beyond the four points above, what is the single biggest &#8220;bad tip&#8221; you see when using RDS PostgreSQL?<br>&#128172; What is the single hardest habit from on-premise or local database development that you had to break when moving your PostgreSQL workload to a fully managed service like AWS RDS?</p><p>I love hearing from readers &#129782;&#127995; Please feel free to drop comments, questions, and opinions below&#128071;&#127995;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/p/fine-tuning-myths-killing-your-rds/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/annpastushko.substack.com/p/fine-tuning-myths-killing-your-rds/comments"><span>Leave a comment</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!rAKp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F438ad492-9bb9-486b-aa9d-af46c5a694b9_3747x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!rAKp!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F438ad492-9bb9-486b-aa9d-af46c5a694b9_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!rAKp!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F438ad492-9bb9-486b-aa9d-af46c5a694b9_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!rAKp!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F438ad492-9bb9-486b-aa9d-af46c5a694b9_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!rAKp!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_webp, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F438ad492-9bb9-486b-aa9d-af46c5a694b9_3747x225.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!rAKp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F438ad492-9bb9-486b-aa9d-af46c5a694b9_3747x225.png" width="1456" height="87" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/438ad492-9bb9-486b-aa9d-af46c5a694b9_3747x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:87,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55907,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://annpastushko.substack.com/i/153973517?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F438ad492-9bb9-486b-aa9d-af46c5a694b9_3747x225.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!rAKp!, /__u/annpastushko.substack.com/w_424, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F438ad492-9bb9-486b-aa9d-af46c5a694b9_3747x225.png 424w, /__u/substackcdn.com/image/fetch/$s_!rAKp!, /__u/annpastushko.substack.com/w_848, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F438ad492-9bb9-486b-aa9d-af46c5a694b9_3747x225.png 848w, /__u/substackcdn.com/image/fetch/$s_!rAKp!, /__u/annpastushko.substack.com/w_1272, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F438ad492-9bb9-486b-aa9d-af46c5a694b9_3747x225.png 1272w, /__u/substackcdn.com/image/fetch/$s_!rAKp!, /__u/annpastushko.substack.com/w_1456, /__u/annpastushko.substack.com/c_limit, /__u/annpastushko.substack.com/f_auto, /__u/annpastushko.substack.com/q_auto:good, /__u/annpastushko.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F438ad492-9bb9-486b-aa9d-af46c5a694b9_3747x225.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://annpastushko.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">If you would like to get such posts in your inbox twice a month - I&#8217;d love you to join the party&#129395; Sign-up is totally free &#10084;&#65039; </p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item></channel></rss>