<?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"><channel><title><![CDATA[Hands-on architects Blog RSS Feed]]></title><description><![CDATA[Two software architects, 15+ years each. Small, task-based engagements — architecture decisions and reviews, and the AI work harness your team owns, not rents.]]></description><link>https://handsonarchitects.com</link><generator>GatsbyJS</generator><lastBuildDate>Wed, 02 Sep 2026 08:38:59 GMT</lastBuildDate><atom:link href="https://handsonarchitects.com/rss.xml" rel="self" type="application/rss+xml"/><item><title><![CDATA[Agent Harness vs Work Harness: One Term, Two Systems, Two Owners]]></title><description><![CDATA[Ask three engineers what an AI "harness" is and you will get three confident, incompatible answers. The vendor engineer means the…]]></description><link>https://handsonarchitects.com/blog/2026/agent-harness-vs-work-harness/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2026/agent-harness-vs-work-harness/</guid><pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsTAAALEwEAmpwYAAABtklEQVR42gGrAVT+AP379cK0qYpwX93X0bG8v7jDx/j07/379Nzi4P379fr6+N7Mq+nfzf///8rGv5eNg+Df2v/79vz48/359ADu6uSGYUqqhGrNyMI/Ulo/UFvv7eju7eeZl5J7dG3b29i1pX7l3sv///+3qp2Ha1SCfXj////9/fb9+vMA////pZOGXEM3vrizramioqCc/P79iJyoYGlqGjhBiJSY7e7u0sG08/TvwqaRlGxR1se65Ofm5ebj+/jzAMXJyb1rPYQxBKhuS6yOd7+0q7CsqIiAendkWJCGe5WJgJ+dmratpHOlu12SrDx0j4OyyFFfZDFLXObk4QBfZmjWeELnfD6WZEexpp3DyMh+g4WNjYyVhXqJcV2CcmeZl5bH0NPg39ujx9ZsjJZSj66Xnp2Qlpfy8OoAz9PShVxMhk8ynZmUzcvHm6KllZufeXZykI2HkpKQjYmDu7y6dH2CYmZpeImRMFtwcouY/////P34/PnzAPH184Kow3+WptHT0b6/vGVrb32Bg76+u5GXmmNqbpWanK6xsGVscLS1s7/BwXiEiqirqf/++Pr28Pz58zj/GE6WlYgPAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Architects fit an oversized brown leather horse harness onto a smiling white-and-blue robot. The multi-armed robot continues typing on three keyboards as code windows float around it, with a glowing lightbulb overhead.&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/35886566e2dc8429e6b8b1922cae09db/3c492/hero-image.png&quot;
        srcset=&quot;/static/35886566e2dc8429e6b8b1922cae09db/ff46a/hero-image.png 325w,
/static/35886566e2dc8429e6b8b1922cae09db/a6d36/hero-image.png 650w,
/static/35886566e2dc8429e6b8b1922cae09db/3c492/hero-image.png 1300w,
/static/35886566e2dc8429e6b8b1922cae09db/d544a/hero-image.png 1950w,
/static/35886566e2dc8429e6b8b1922cae09db/6976b/hero-image.png 2172w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;callout-note&quot;&gt;
💡 Deciding where the AI budget goes rather than writing the prompts? Jump to the &lt;a href=&quot;#tldr&quot;&gt;TL;DR section&lt;/a&gt; for the executive summary.
&lt;/div&gt;
&lt;p&gt;Ask three engineers what an AI &quot;harness&quot; is and you will get three confident, incompatible answers. The vendor engineer means the orchestration loop and sandbox their product ships with. The framework author means every line of code that is not the model. The developer down the hall means the &lt;code class=&quot;language-text&quot;&gt;CLAUDE.md&lt;/code&gt; and the custom linters in their repository. All three are right, which is exactly the problem - the term is going through what Martin Fowler long ago called &lt;a href=&quot;https://martinfowler.com/bliki/SemanticDiffusion.html&quot;&gt;semantic diffusion&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The cleanest anchor comes from LangChain&apos;s Vivek Trivedy: &lt;a href=&quot;https://www.langchain.com/blog/the-anatomy-of-an-agent-harness&quot;&gt;&lt;strong&gt;Agent = Model + Harness&lt;/strong&gt;&lt;/a&gt;. &quot;If you&apos;re not the model, you&apos;re the harness&quot; - every piece of code, configuration, and execution logic that isn&apos;t the model itself. Birgitta Böckeler &lt;a href=&quot;https://martinfowler.com/articles/harness-engineering.html&quot;&gt;adopted that equation&lt;/a&gt; and immediately flagged it as &quot;a very wide definition, and therefore worth narrowing down for common categories of agents.&quot; Addy Osmani &lt;a href=&quot;https://addyosmani.com/blog/agent-harness-engineering/&quot;&gt;arrived at the same place independently&lt;/a&gt;: a coding agent is the model plus everything you build around it.&lt;/p&gt;
&lt;p&gt;We agree with the equation, but the narrowing is where the useful insight lives. Most published material obsesses over the inner layer - the layer you mostly &lt;em&gt;buy&lt;/em&gt;; the outer layer is the one you &lt;em&gt;build&lt;/em&gt;, and it is where your leverage is. In this post we split the harness into those two nested systems with two different owners - the &lt;strong&gt;agent harness&lt;/strong&gt; and the &lt;strong&gt;work harness&lt;/strong&gt; - walk through what lives in each, and then use that vocabulary to revisit the &lt;strong&gt;Delivery cluster&lt;/strong&gt; of our &lt;a href=&quot;https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/&quot;&gt;Harness Model&lt;/a&gt; (in short words the execution - how work gets done). The punchline: the jump from Stage 3 to Stage 4+ is not a smarter agent. It is a change in &lt;em&gt;which harness you, the human, are talking to&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;two-harnesses-nested&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#two-harnesses-nested&quot; aria-label=&quot;two harnesses nested permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Two Harnesses, Nested&lt;/h2&gt;
&lt;p&gt;Peel the buzzword apart and two layers fall out - with two different builders.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;agent harness&lt;/strong&gt; is the control layer the vendor ships around the model. It contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;System prompts&lt;/strong&gt; - the behavioral baseline you never see&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Orchestration&lt;/strong&gt; - subagent spawning, handoffs, model routing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Building-block interfaces&lt;/strong&gt; - tools, skills, hooks, MCP support, and their descriptions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bundled infrastructure&lt;/strong&gt; - filesystem access, sandboxes, memory, browser&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The user interface&lt;/strong&gt; - terminal, IDE plugin, web app&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This component list converges across sources that have no reason to agree: &lt;a href=&quot;https://www.langchain.com/blog/the-anatomy-of-an-agent-harness&quot;&gt;LangChain&apos;s anatomy&lt;/a&gt;, Osmani&apos;s restatement (which adds observability), and the architecture taxonomy in a &lt;a href=&quot;https://arxiv.org/abs/2604.00189&quot;&gt;138-talk practitioner study&lt;/a&gt; all describe essentially the same machinery. Anthropic says it plainly: &lt;a href=&quot;https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents&quot;&gt;&quot;The Claude Agent SDK is a powerful, general-purpose agent harness adept at coding...&quot;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;work harness&lt;/strong&gt; is everything your team builds on top of that, for your codebase and your delivery process: &lt;code class=&quot;language-text&quot;&gt;AGENTS.md&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;CLAUDE.md&lt;/code&gt; files, skills, MCP servers and CLIs, hooks, the tests and linters wired into the agent loop, and the workflow definitions that string them together. The vendor cannot ship this layer - it does not know your module boundaries, your compliance constraints, or your definition of done. Stripe&apos;s &lt;a href=&quot;https://stripe.dev/blog/minions-stripes-one-shot-end-to-end-coding-agents&quot;&gt;minions&lt;/a&gt; are the textbook example: &quot;our custom minion harness tightly integrates with that tooling&quot; - the developer-productivity stack Stripe built for its humans - because &quot;if it&apos;s good for humans, it&apos;s good for LLMs, too.&quot;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Terminology, openly.&lt;/strong&gt; &quot;Work harness&quot; is our coinage - no published source uses it. Böckeler calls this outer layer the &lt;strong&gt;user harness&lt;/strong&gt; (also &quot;outer harness&quot;), and her name for the inner layer is the &lt;strong&gt;builder harness&lt;/strong&gt;; the &lt;a href=&quot;https://zenn.dev/r_kaga/articles/329afdc151899f&quot;&gt;Zenn writeup&lt;/a&gt; that names the split most cleanly calls the two layers &lt;em&gt;Agent Harness&lt;/em&gt; and &lt;em&gt;User Harness&lt;/em&gt;. We prefer &quot;work harness&quot; because it names the &lt;em&gt;work&lt;/em&gt;, not the user - the layer exists to make a specific body of work deliverable, regardless of who triggers it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One boundary case before the picture: teams that build their own orchestration - with LangGraph or similar - own both layers, and the test is ownership, not vendor. Whatever your team builds and iterates on is your work harness; what you consume as shipped is the agent harness.&lt;/p&gt;
&lt;p&gt;The layers nest: the model sits inside the agent harness, and both sit inside your work harness.&lt;/p&gt;
&lt;div class=&quot;diagram-desktop diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 24.923076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA+klEQVR42i2PzU7DMBCE8/5vwYETN87lBAcESqmAQoBQmjaJ8+/Yu7Zjx3ZwC9JotLPSaPeLBrRMWK4cKMfleZaWA+Sk3ufVkTRFWQ19R4eO0V5wJpELYABsAB1RtPvOvefiNeNflSbMT9q4qV506sWLh2fT3asm1uOODZ+tzDp1HKbCG4LIox5d/KNWa7Jal7fbPm2cmrRVrYe4Sy7J5mLcXXncLG6sTHVDP67zpzv4tqYG4FF4O63ncHl7wLejKKhT2lhVeZGY/mGm8UwfPSaLqdVUNDILatXB6/K/DGdgmPwf9kmIgIhSghAgJJxi2PEArBCCh0jB/AJKxhZC5dqSOQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Nested harness layers: the model sits inside the vendor-built agent harness, which sits inside the work harness built by your team from guides, sensors, and workflow definitions&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/febbcc58bcd002d7557bd86fe47d68ce/3c492/harness-layers.png&quot;
        srcset=&quot;/static/febbcc58bcd002d7557bd86fe47d68ce/ff46a/harness-layers.png 325w,
/static/febbcc58bcd002d7557bd86fe47d68ce/a6d36/harness-layers.png 650w,
/static/febbcc58bcd002d7557bd86fe47d68ce/3c492/harness-layers.png 1300w,
/static/febbcc58bcd002d7557bd86fe47d68ce/d544a/harness-layers.png 1950w,
/static/febbcc58bcd002d7557bd86fe47d68ce/813c1/harness-layers.png 2004w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-desktop diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 24.923076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsTAAALEwEAmpwYAAABUElEQVR42jXQTU7DMBCG4V4ApBYoddrQ2o7HdhInJf1Jf0BQBK1ArFiwg3MiFohDkA1WBZwD3MEqYvFodqNPby2Uo9eAZ5+Em/cWNZawzAZR7mWWQmqLcm7H84UdlCd2MJ3bJO9bbTKr08yCTGwEygqI3znoryM9fKm1RfERJnOE4eUmLlfI8nNsyxEGPYWStTDu7aJhdczFHk4MwVk/xEEcYJGFCIpgBAS5aDrVayCTyboWQLGG8hanN48/s5sHV1zcuzA9df6hA9pyeVR312exu1sdu4tJ5FJedzzYcR3edL2JceZy6tqF/I6624d2u5D2F37h1UaNl/4usaMn2KYKFSeYRQ0s0wCHcQtHXib2UXXrCPwAhQ4QPKGIU7SBXJl1raNHz4Smvl/6dkjTijBT+aZbFJKKga6oUH9AVUzqiv8DWXEhq0jIN+Zb+oZPv4RZi8+kVT4xAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Nested harness layers: the model sits inside the vendor-built agent harness, which sits inside the work harness built by your team from guides, sensors, and workflow definitions&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/735f9a34675f9c3cbc7066f981e295fd/3c492/harness-layers-dark.png&quot;
        srcset=&quot;/static/735f9a34675f9c3cbc7066f981e295fd/ff46a/harness-layers-dark.png 325w,
/static/735f9a34675f9c3cbc7066f981e295fd/a6d36/harness-layers-dark.png 650w,
/static/735f9a34675f9c3cbc7066f981e295fd/3c492/harness-layers-dark.png 1300w,
/static/735f9a34675f9c3cbc7066f981e295fd/d544a/harness-layers-dark.png 1950w,
/static/735f9a34675f9c3cbc7066f981e295fd/813c1/harness-layers-dark.png 2004w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-mobile diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 676px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 190.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAmCAIAAABLWSz8AAAACXBIWXMAAAsTAAALEwEAmpwYAAAEeElEQVR42pWWiW7iSBCG8/5PsdIec+xqLs0MIceETUgg4IQrIWBsAz6w8X3bbfexZWCiZCaJZq1fperu+rrK1S3Le6pLVBevPKx7RNTTE27RvlldTa3Ta7lxLQ9mTm9mD0W3LziXt8bcyCAMgjWQS/aAzBDOC1wSEsTpVFLn6nqhmWNB4eeaajizxUqUDZgEBwJKTLICFyWGrHsrtyLDFI+1kjeIZFPRonOHyT5buEyy2dKtHJgETQ18q5VRhksMycme5lbbWCG+XpKZSScGGeuYE6PmrX09Tzgpbk08ToxhEpYgoDsnXozhqcrewnaE+wqBvWF5uEwOOeXD6bTLe6dD40tL+tbTbtVcMKvkkMNLnoEFi9Y7y1p7DvbsxgR70JVr7cW33krYVH61/DlzWA5Ueqehj80ZRAMG2QbLuDHQG31tomdQS70jT/WyJ9Mnyh6qRLLIWMuPr5R/hzo380562iEnn/TUI7D9qvK5Tfsy8X8oGxp2OYdW0VudnU+C80nYWxYX06jNR807vzFyW9NopNGxQS/Ex2XDUUH3+TUWzEqwPQiqWDiVA3brbFd5A8fbo9rCcElQUY13KjfCD+z9zEaQDJUP4LwoM/TLKsriEVzSvGQ5ZoiwnFS2Eq6Uf9fOh7CS3sMkyxEKNRQqqTuPLSG2Z6kjReYstgWYybzFdytl/hLC8lAtirKCVx5LEx+7fVYo6/HBov3Wmhzqwy/C6R/K9UdzcqT1P4PjCA2Fe29PjxlSsHOF8kTz2A4mkcBoRHFEScRYQikoZSSE4U5064SMhjjgH8DxBi504rSp1yFeh/pd6nM04FjAUf8KfOJ2qNclbpsVaxxMUR7fwx6JJZYKxPxCgyb1Gpmxn+m1fH2Qbx2jTvwGLJH1J5YtcTj7GRaJc0QTjoQtrf+XNnhl8x/04ZvV4LU+eoODi2rJrrMcYP4p2D5gSdeevlsNX+mj1+r172AzqwHWGL1m2RWxak/DNBWpe4zcJoQuO785sw+hsq/1/jRu3gTLr67wMbNOmXOwKfsJWCD2PvbPYvUrKFsfFs5JotVSvY7Mo9w8wn6TWp9/fudttw3itKpW75rc3fqbzne3Q+JcsPLHbvsk5BnxWOkxDNathDe6dyptVomH/bvH5xzOGHHhDFlpvqg1ww4OfoT5DWzS0npBFY/dZ2HY+HnZz8PVG5pJvgoyNcw0EDgPfC3JdQbJAfafykyLddOftBKpHYuXidhJ5yDwN8P5Zcjn+erJhlUwQetmNOuXK57ZrUD8e3r+D3/Ry9QJs0ZY7yZSnGkAl8/BF5HQR+q+2qurfYDfTs7eC+1j42aANC6Rkpfhb+G0jZSaMTywbvfXo9p69G7J1c2bS6Scx8JLMHR7FAr9SLzLluN0cZctxtlikst36WIQieNIxMiAsMfvvPuS+Ky0q3tGvN3FKp2dwN/Ow2kRf3M9H2b2bxhasUymufKCWC4zpGF3cH+34euZI18qgl8V8kVUIAC//1Zsv9u/Krr7rVBcIttYc/6fFKeC/wM4uDeX86VR8gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Nested harness layers stacked vertically: the team-built work harness contains the vendor-built agent harness, which contains the model&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/1c181eed6480e1ca876719f2210bd328/9bb7a/harness-layers-mobile.png&quot;
        srcset=&quot;/static/1c181eed6480e1ca876719f2210bd328/ff46a/harness-layers-mobile.png 325w,
/static/1c181eed6480e1ca876719f2210bd328/a6d36/harness-layers-mobile.png 650w,
/static/1c181eed6480e1ca876719f2210bd328/9bb7a/harness-layers-mobile.png 676w&quot;
        sizes=&quot;(max-width: 676px) 100vw, 676px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-mobile diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 676px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 190.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAmCAYAAADEO7urAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFpUlEQVR42p1W2W7bRhT1D3iLZclcRHERKS5aSO2yKFmyJUuWN9mOWztx4yROkCDtS4E+FijQ9yAvbf6ggfrc9NtagAZOL0eS7SZxYuTh4N6ZuffMXYbDmbHWzy9kd/297HX+EDNro7Q/HJ18/+tocP7TqP3Nq9HRi59Hxy9/GTUPno/aRy9H9b1nI5/0/tmPI7O+O5Jy7VHom8i1/zI3njya0co7bwXNAZdIBbG4DjmVQ2VtAK+2DrfSRrW9jXKzj3SxgVKjh0K9w9ZCPbRdIR9ONgNeMaHV9t7MqKXe75ycAqc4/9JOl1KmdclZ/iVn+5crZv0yolcuY6lVNhc1qkzn7QaDlG1dyl73kpPtfzkpCbU8eD2jFrfecgkDgu4FcnEA0WkgnmmBUoC9uov8+jHMygBWbQe51iGbk7JtZiOQrVLaBa9lg5V4kvTBmwlhCkLSDRL5PgS7zgyNUh+N3XMMHvyAbGOIau8UneMX8Le/g5rfYDahbRgEr2U+JBxHeEVo+2gNn2Lj6DnW9p+ivHlC4ws0955g/fAZVgdnROhPCLc/FeGYUPJ6UN02et++QouISt0T5pwq91HtP0Rt6yErRXP3MdtAzlJpClufSdnbhGivQvU20Nh5hFr/lNLdozTPsLb3GPXBtdTyHQhWDXKhP0lZ/zhCtbxPxV6HmF1Hdu0+MmtHUIt0ZJpHcPwDZFvHKHRP4TQOIeU6zFatHnwcIS+bdJbsgI4BpEyTQbDqFG0d8XRjUtcxeGuVyaldWAJOtgJOMiaEhR4RWojFk0FM1DBFuGOIa/16bjp/ZSvpQVg2pbQVRjgm5NikgTFSuNY/Nf4AksF8rwgpZCwLRrAsprAkGAR9Igm8zsYR0iPiDUzGoc+yaFynLFPKgmLBSmmBYypw0wYKroV8zoSXSaHojfVcWkfW0UkaJJNwMwYbO6YK29QCfpqy6PXeaoaDWnolsBKzOOymcXG/hoOOg5OBi5cPGjjbK2JI49Ntj+k7LROPhiUMNxzYiTnUs1wgqSk6QjcIc6lYIHFzSHDzDPGVWcIcxNgsZH6BQQrX+BsQFkjOIW+tBAkilK4IUw5cIjSkeaxmIqg6EdQIFXuJoWzdu0KFUE2PbeqZZejkMyWUbxJm9WiQ1RbhZ6JIa0twjWXUciJW3TgqGR7VrMD0ULp6hGwiaOaicJQFeCYRarcQlu1lGIklOORw0Msz9Bo29jsuhpt5kh7SydDmHtV9GfathMaYsOKMjftNmwg87HddHA9K2OvkULB5Nt4nPRlfoPJ8LsIJYdGKIGtyRObidL+KTd+GX1Bw2C8QqYu1chKdegqeLVA2S19OOSx2Ro+ikZfhEyoZISw66l4CNaphKc0zZCjtsCm3Ek677JNRzaHOmosMVepqLey2tchQJT1EaONnb+synUOPCBU6W6q4wKDFJxAniP9/bmqnCPMoEKH0KUKVFg1pgXa9G0JbdUKYuI0w7F5SuiPidyUU5r4McZ6R3olQ16jY1OkpDGOMm3M6HfwvRqjQzrq0iETVBO8lIeZ1CCS5nEZQmR7O8W4SiZIOQ41AocvhdkJaNBKLEIt0eRoS5hIxyHkL/nAT9b0OViwFCwqHSDIOwdVg6MtQuNnPR8gICzqilgx3o4YcwT/YRONwE9WdNpxmATFTHhPS4Vb4uxBWLEQdDVarDN33YK4VSS/B22ogRYThmlgw7hLhPPSw0AWVUlUhpuP075WYFNMSPT9EiDROeCq9MBTo8j1qyi01dI3xl8K+CqpliKQYHiHqvDCRk/F0PbQNffIffSkpm26OaGBKc7DleXbH3Q3zCH2q6dj1L4DeJr/FVQuuo/yTs6TgaxD6CrIRPu1esycxr9iIxulJLNEL4isQjZvsN6pVdt7MmJ3Hz5R8928t332nFrp/fg204uY72e28N7sX5/8Bmvo+HTeupaYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Nested harness layers stacked vertically: the team-built work harness contains the vendor-built agent harness, which contains the model&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/735c4d16faea912794c7785c9c3b43a5/9bb7a/harness-layers-mobile-dark.png&quot;
        srcset=&quot;/static/735c4d16faea912794c7785c9c3b43a5/ff46a/harness-layers-mobile-dark.png 325w,
/static/735c4d16faea912794c7785c9c3b43a5/a6d36/harness-layers-mobile-dark.png 650w,
/static/735c4d16faea912794c7785c9c3b43a5/9bb7a/harness-layers-mobile-dark.png 676w&quot;
        sizes=&quot;(max-width: 676px) 100vw, 676px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Most of what is published today - benchmarks, orchestration tricks, context management - covers the inner layer. Useful, but as we said up front: that is the part you purchase. The work harness is the part you engineer, and the rest of this post is about how.&lt;/p&gt;
&lt;h2 id=&quot;inside-the-work-harness-guides-and-sensors&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#inside-the-work-harness-guides-and-sensors&quot; aria-label=&quot;inside the work harness guides and sensors permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Inside the Work Harness: Guides and Sensors&lt;/h2&gt;
&lt;p&gt;Böckeler&apos;s decomposition of the outer harness is the canonical one, and we have not found a better axis. Everything in the work harness is either a guide or a sensor:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Guides (feedforward controls)&lt;/strong&gt; steer the agent &lt;em&gt;before&lt;/em&gt; it acts. They raise the probability that the first attempt is right.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sensors (feedback controls)&lt;/strong&gt; observe &lt;em&gt;after&lt;/em&gt; the agent acts and feed the result back, so the agent can self-correct before a human ever looks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each comes in two execution flavors: &lt;strong&gt;computational&lt;/strong&gt; - deterministic, fast, CPU-run - and &lt;strong&gt;inferential&lt;/strong&gt; - LLM-run, slower, more expensive, non-deterministic. That gives a practical 2×2:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Computational (deterministic, CPU)&lt;/th&gt;
&lt;th&gt;Inferential (LLM-run)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Guides&lt;/strong&gt; - steer before the act&lt;/td&gt;
&lt;td&gt;Bootstrap scripts, codemods, language servers&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;AGENTS.md&lt;/code&gt; conventions, skills, reference docs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sensors&lt;/strong&gt; - observe after the act&lt;/td&gt;
&lt;td&gt;Linters, type checks, tests, module-boundary checks&lt;/td&gt;
&lt;td&gt;Review agents, LLM-as-judge, semantic duplication checks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;You need both directions. As Böckeler puts it: &quot;you get either an agent that keeps repeating the same mistakes (feedback-only) or an agent that encodes rules but never finds out whether they worked (feed-forward-only).&quot; A well-built work harness serves two goals - it increases the probability the agent gets it right the first time, and it self-corrects as many issues as possible before they reach human eyes.&lt;/p&gt;
&lt;p&gt;The two controls wrap the agent from both sides - guides steering the input, sensors observing the output - with the human tuning both:&lt;/p&gt;
&lt;div class=&quot;diagram-wide diagram-desktop diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 16%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAmElEQVR42h3OTQ6CMBBAYe5/LBMWbtTEGIwLDQGxGlss/ZvpTKei37vAa0SqlLUfLkIspUj9ixliTqUUT9AnPSQdCCFTQAlYUpZmOrvrzugp2Az7W2i7j7J5xhCjYr2l1yb5QSMe3Xhyd4P+ueChh7abL4obCByXjImR2Xh6LxSRgYkZrVPGjsKwXjhMHlKVilRfCz9MNkG+Zz2rJoJ0TW4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The steering loop: the human improves guides and sensors; guides steer the agent before it acts; sensors observe the output and feed self-correction back to the agent; recurring issues escalate to the human, who hardens the harness&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/7cf8d08f12501855608e7e81488b8d97/3c492/steering-loop.png&quot;
        srcset=&quot;/static/7cf8d08f12501855608e7e81488b8d97/ff46a/steering-loop.png 325w,
/static/7cf8d08f12501855608e7e81488b8d97/a6d36/steering-loop.png 650w,
/static/7cf8d08f12501855608e7e81488b8d97/3c492/steering-loop.png 1300w,
/static/7cf8d08f12501855608e7e81488b8d97/d544a/steering-loop.png 1950w,
/static/7cf8d08f12501855608e7e81488b8d97/d2433/steering-loop.png 2600w,
/static/7cf8d08f12501855608e7e81488b8d97/2b2b8/steering-loop.png 3344w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-wide diagram-desktop diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 16%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA8klEQVR42mOwt69n0dIKZauvr2f5//8/o5JxKL+MhZuQh0cuOwMU2LraKpo626itWrWKWd/fXUHBTLdUwUIv19jHWs4nMk1E1TxcU9UuXFPFOECZwUjddauZlttNUx2XCn1fX20tm4C7eg4h/5XN3YtsQv20LExVomz0pHbZGUgdtzJVTtECiilb6q5VtjaYp+tja6Rl5xOlaub539Ap9L+8vsttBhsDPy0bowBjF+tQOX1/fwFt22A3HbvQQE27IFWbKG9BZ2dTYTtH/TB7J+MUNwsZIZCLbYKClGx9QxVBbDnbQEllM89sHdugHCVjtygAPAc9lsTNNc8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The steering loop: the human improves guides and sensors; guides steer the agent before it acts; sensors observe the output and feed self-correction back to the agent; recurring issues escalate to the human, who hardens the harness&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/0f113f9950880a24a35beb9de2768f42/3c492/steering-loop-dark.png&quot;
        srcset=&quot;/static/0f113f9950880a24a35beb9de2768f42/ff46a/steering-loop-dark.png 325w,
/static/0f113f9950880a24a35beb9de2768f42/a6d36/steering-loop-dark.png 650w,
/static/0f113f9950880a24a35beb9de2768f42/3c492/steering-loop-dark.png 1300w,
/static/0f113f9950880a24a35beb9de2768f42/d544a/steering-loop-dark.png 1950w,
/static/0f113f9950880a24a35beb9de2768f42/d2433/steering-loop-dark.png 2600w,
/static/0f113f9950880a24a35beb9de2768f42/2b2b8/steering-loop-dark.png 3344w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-mobile diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1022px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 137.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAbCAIAAADzvTiPAAAACXBIWXMAAAsTAAALEwEAmpwYAAADPElEQVR42oWU2XbbNhCG9f6v0dv2Iue0UeQ6iRtbcSw5WmxtFC2SEsV93wEQGAQSXS2J3OKAIAHiOz9mMDMtft4YBUcu3EUp+kvfX49iX0bWtAj1avcbzja3foIpYc6i0PrR9Is1+Hsz+qgv7hx9kAba/8EADCMilNXv4fKba00ybRDOupY+Thw1Y5xdhhkDTGpC6ppSgliNAWFqBrEbZ7QGsYKqmjJGxcPgDBYrCBOA19XmlZHywZAm/rpm9MwutmtHGGNymIvmJqAHYMVc94noZrSbJsVRUOi/wkIQE3L0NnDJhoUtRvZ5bN9LkezxqQmbAPZOaWxkzTFbjbVHmPGVB5JNb5+tTk/7NDQeX+KlA0Z0EQYQbjqBd7JdKWv3jU9j78s0uh46jxrZBPyyMjlRFs1LwUkhKrmXMSdlQcmtGJISDr58E4b9BiMJhrY6MJWJrw9sJcfVaYC8rSxWGSqqNCpCJ3a91BMfBOecYf4rTCitMIbDBZMQ3Pc86/LsjthXtfeR5195/A8LbjlFYgcVUUhrcfnijK1lYN9ocz0NXmkSsvA63XZy40of/ObOf0837cK+hvSB06qo676l3OvyvSFLgdUS8VhUiO2O0ShH3O8go10Z75H1AZntRHmHzTaEd7BXFpqIEFzvovVoc+MqRHFYBEEeMFpxQMLUqsxqXHBGDi674LBmHhT5N/Vl7pinl4dITQH4RW83EeaSXEHBtk7UMjBpusHRGkd4nxW7XKP0Moz2sf1cWreV0s1Xf2gPV97kodK6hRqR0itSNXaVyHWK5E14hpzPidSxRu/Wvb+MYcd+6qG1gLXIm3jbJ0dfJ94hK1/hvUk7eIPjcWHKxJexv6rDOXKnpV1S8uuxRUoeYZEYwiPNDz3yp9Zm6VklwYcrEE6h/5YEgR3zuXkJXuiL0cniTerLvtXEc2Nhk/B77OwIpwUQ4gzPDKr5XDLpdFsvLCb6zKRzU4xMdhgAXCiAzVqJ+XjLVj4oIZ+Z+M+vys2TpQRcDfnSg6XD4GL1PMATE4ZqNlLzRzkRZeRmbPbluCdFT5vqxQP4j7pdET4xBJwP1WKklc86Gmvl91XWW8bjNVq5P8M/AEHfCYi7NaiaAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The steering loop shown vertically: human improves guides, guides steer the agent, the agent produces output, sensors observe it and feed corrections back, recurring issues return to the human&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/ae52b678f9629a9c2a9fea5566abe1bf/1ac29/steering-loop-mobile.png&quot;
        srcset=&quot;/static/ae52b678f9629a9c2a9fea5566abe1bf/ff46a/steering-loop-mobile.png 325w,
/static/ae52b678f9629a9c2a9fea5566abe1bf/a6d36/steering-loop-mobile.png 650w,
/static/ae52b678f9629a9c2a9fea5566abe1bf/1ac29/steering-loop-mobile.png 1022w&quot;
        sizes=&quot;(max-width: 1022px) 100vw, 1022px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-mobile diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1022px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 137.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAbCAYAAAB836/YAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFIUlEQVR42o2VbWhTVxjH0xelda3GutamzVvTNEnTvOfem7eb3Lzc3Nzcm+SmyW4TozWaatx0VXQKDoaXjTGnUmRbmWR+KIx9GNlEEDZhyvLNbVBhMBWxQ5jM4caYHzdo9OzeNOlSa2cPnHDuyf/8nuc55zzPEYlWthbhBwDQCmnJsmYQ+WVU5rrjMdF37Tr87ogEvq2Xe371GVLHBB0rYttEL2gNYJtRif1gVKHAZSSrfpgBPnsCwGN4Va9wA2SUPr1eoIhl2bYHN0CnXUtcgnX4A5+V+cmiCtx0jpI/huD0bdMw9jAAZ9+qq9cGlsvltlLpyqaZmXInx5U3fsB9v/kIMyeO4Yfl8cw+Jjo+FUYt073cVLmn9P61LRzHtZdKpQ3CulUwjqu0c7PlLl7U+t+kqDZ2EYQOTRCXHFTgPMayXc1bIzQBKqxfATx37tOXmizVxAbiQFjjzx0a9uzYrQ/k9wt9xJvbo/HvPGiIHdM1a0ul+Q3LRgSvOD7UJn4NrHImL8vNISAzBhed0Ulg8b8CBvTeqtIaBjoeXt/wmpar8BE2ohM8mymXO5ss1kQKG/WlwugHTjL7d3C8sOihJxeNaPwfmQEDam9uagWQexY4sxqodKQvmwIZ4E0WFxFyN7DhO4CT2lPVuBgwEijsXy9weV/GiGJAASULW0epiX6ImZQjqcmXx6KZIQ87ZUoe0yxJQct6gKLGn0QmHXPFI58E08kPoYj/I5ShLkQyjLL5FqwLyLKithMFbTdNe8xu3E6FYkTaF/GlPEFHNJdyKY6yzs7mq7MCyGdGVzw7tZ0fbgR10W5SNoxZen+3qsX3Ic2WO3774M+oafuCVd19y6bp+TNo77vKpyev5Vq1bm03xjBiM4aJFQpFhwihiFNOirjliOJ7GxZ3YBIdpO8DHvswcFuVgKURQAeMwAsNVx1mBbCPbrsp6IYxTA2FA1cdNHkNIoI3eM6pmofZw4e36/X6ZQ/3hvpUflPPPRyRLwQRxW+8h3z+Sh9SXs0fmF36l9/a+3XDQ3ci0Z3LvbaVyefFWD7fsXIP69fAG/FKUAKOYQRGToZ02xhsizjhFnVP7xuXvror3FcsSjatSt3nHUpjkpyYgHE28xmeYk8+m7tCmtZzt+lQuJVArlSqWZRFYWgwYC72ObU5KWo5KIbUuwZQw94B3DZhL8Zqmvn5+Q2VCmj/Xw8LJ85014B+y1mJTQNkdt0iFMOA1md7IjGpgQTSPtIeyg7AoVAMigSPOKKRIw4Sp9YETnPcZmEs8RjOqlxGYAw5FiHKB8y4+4khgIB+68gj64nsgJPAX4fCwY8dFHEBocIHloHNxaE2MTtbq3MyEooPoqaL2+CRt3sR7Ts9kPpUv2P0vBQzvaue3rm57sDGerlabhUeuHTq9TY3N9ch9MY3mcmw/vHx97yx2MkgSw823YDWgxzXlec4QdvaeDKEIru6as9VOgRPj87MdPpSqQBMh/MhNlVMFAoDjZMWvOB7e/0haxFAdVjL81672mTujVmFypP9XGqmvlAh41fUruS3Q0jiuhKKX1c5ExWVk/lmCI59N+Lbdbq+tnWNZ2ppD/Ts8f4hKPp4UI8Cic79dMgSAjh7gC+0OdCvdfPdVZUaMaCE4181V/i1gdTxfrmNeKx300DnpJ4a0QTgqzVwRXcCYWz2JasahARyW/RFwKWQLeybvUo4cXMMTd4f86bv6TzJBY2DXtC5mQWjP71gxbN3NQj9SIkkLz4v5H8BYXnzflabFqQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The steering loop shown vertically: human improves guides, guides steer the agent, the agent produces output, sensors observe it and feed corrections back, recurring issues return to the human&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/6a9307835bbcad232397bac430fd850b/1ac29/steering-loop-mobile-dark.png&quot;
        srcset=&quot;/static/6a9307835bbcad232397bac430fd850b/ff46a/steering-loop-mobile-dark.png 325w,
/static/6a9307835bbcad232397bac430fd850b/a6d36/steering-loop-mobile-dark.png 650w,
/static/6a9307835bbcad232397bac430fd850b/1ac29/steering-loop-mobile-dark.png 1022w&quot;
        sizes=&quot;(max-width: 1022px) 100vw, 1022px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Where do workflow definitions - slash commands, pipelines, triggers - fit? They are a &lt;em&gt;component&lt;/em&gt; of the work harness that sequences guides and sensors into a repeatable process, not a third axis. That distinction matters later, because workflow definitions are exactly what migrates as teams mature.&lt;/p&gt;
&lt;p&gt;We have already built one of these in public: our &lt;a href=&quot;https://handsonarchitects.com/blog/2026/harness-engineering-for-e2e-tests/&quot;&gt;harness engineering for E2E tests&lt;/a&gt; post is a worked example of a guides-and-sensors stack - scenario corpora as guides, pytest-in-Docker plus reviewer agents as computational and inferential sensors.&lt;/p&gt;
&lt;h2 id=&quot;the-steering-loop&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-steering-loop&quot; aria-label=&quot;the steering loop permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Steering Loop&lt;/h2&gt;
&lt;p&gt;If guides and sensors are the parts, the steering loop is the operating principle. Böckeler:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;The human&apos;s job in this is to steer the agent by iterating on the harness. Whenever an issue happens multiple times, the feedforward and feedback controls should be improved to make the issue less probable to occur in the future, or even prevent it.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is the same practice Mitchell Hashimoto described as engineering the harness so the agent &lt;a href=&quot;https://mitchellh.com/writing/my-ai-adoption-journey#step-5-engineer-the-harness&quot;&gt;&quot;never makes that mistake again&quot;&lt;/a&gt; - the founding move of the &lt;a href=&quot;https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/&quot;&gt;Harness Model&lt;/a&gt;. &lt;strong&gt;Your job is not to babysit the agent through each task. Your job is to notice the &lt;em&gt;second&lt;/em&gt; occurrence of a mistake and turn it into a guide or a sensor.&lt;/strong&gt; That is the loop the diagram above closes: recurring issues flow back to the human, who hardens the guides and sensors.&lt;/p&gt;
&lt;p&gt;Two warnings come with the loop. First, it never finishes - Böckeler observes that building the outer harness &quot;is emerging as an ongoing engineering practice, not a one-time configuration.&quot; Second, stopping early is the most common failure. The &lt;a href=&quot;https://zenn.dev/r_kaga/articles/329afdc151899f&quot;&gt;Zenn article&lt;/a&gt; makes the point sharply: if your work harness is &lt;code class=&quot;language-text&quot;&gt;CLAUDE.md&lt;/code&gt; plus some skills plus a few hooks, you are missing the computational sensors - linting, type checks, verification loops in the agent&apos;s path - and you are missing measurement. Can you demonstrate, with data, that your harness actually works?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Guides without sensors is vibes with extra files.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;driving-the-agent-vs-running-the-work-engine&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#driving-the-agent-vs-running-the-work-engine&quot; aria-label=&quot;driving the agent vs running the work engine permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Driving the Agent vs Running the Work Engine&lt;/h2&gt;
&lt;p&gt;Now the payoff, and a part we want to flag honestly: what follows is &lt;strong&gt;our own synthesis&lt;/strong&gt;. No published source maps the harness layers onto a staged maturity ladder - what the literature supports is the steering loop underneath it. We think the mapping earns its keep.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/&quot;&gt;Harness Model&lt;/a&gt;, the early stages share one trait across the Delivery cluster: whenever a human works with AI at all, they interact with the &lt;strong&gt;agent harness&lt;/strong&gt;. You open the terminal or the chat window, you prompt, you watch, you correct. Your prompt &lt;em&gt;is&lt;/em&gt; the workflow, and it evaporates when the session ends.&lt;/p&gt;
&lt;div class=&quot;figure-portrait&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 682px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 150.15384615384616%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAIAAACjcKk8AAAACXBIWXMAAAsTAAALEwEAmpwYAAAEAUlEQVR42o1VW1MbZRjOf3DGi47jH7AX4k1vKhZFDgpc2BLqjOMVCoz1Qi0zemHGaYFhqBWSFAYTcjAHUkrIYXPa3WxOJCSkSKWmrci0ViA0R3KAJJvDJp/fJiUCCZSdZ3e+fTfP9z77vs+bZRTzZAV5MhUO+PNkGoBikcrDK5ne20vE9pOJWDQEinkql6n+uAIGPGEUULn0Xvzra1/dGhv75edbfN706MgQZjIo5LJZmVTA5wEA4O51yBClQhY+Qw06M47KZRIWiyUQCLc2n29vPvetPVhbXYFCDjJnjpMrAKBUKlEwycqSFZvnB/ybdKRYgDjMqU+GyQGVDQSC9rtjKecoqhTBjQpkqpBN1zKPk2n9sAQUNX37xo+97Xr1vQq5LrNeZgAQjercG2++9vq5hoZ31p/4oPIzZa6QR4Zvnj//1vtNlxrebtAjmrp1rkOmewaoZxvrs3LpvbuwTdLMfgIUsq+UfaSYBI4KBXwrgcP+13rj5ILBhmfTGGqUSyUWM1bDzLyKTO4TODYzw7fbLEdLlalLroYyFavhJgN8YYfddlKdq+TMYUkvyahRJBTYrMQBOXPGdyYLORLHUK1a5Vp0FE5OC3esaVUxH4+GtOoFNnvcqEcSu+FTCn6EDEXSU+F1y6SS4Au/WCRcWXbTDj3FYXCWqWwZORrFQpYwY499D+G1BNMexMuoqTYcO3BwlMrreCwmk8qSiUQ1Un5GHZtNmvxozetxot4lYtmJux2o22HyunB463HiLrvJs0hHfvdYfWurfz32wTGp5mfsRnZ1050hovlfY0vI0hp3tgeJVj/WEra2RmxtUXubH28JEG2++UbNPE+tUuUye7AjL8mRcNSjZIINJlj/dGXuQ+HQBYugySVt1nLfNU03GqYu7t2/DJ5+FlnqsOEKDDeT6SQcHkDrJxnhUGRR0gn+/Kj0oPOZ7pKdf2HL9MEO3ryhbXqqfc83fzHlbgcPu15gjRaj3KA3RkM7sJfwfxaKZ2TTKQmPNTPeK+AMSKYGFLxrIk6/kN0nvjMg4vaLJ/t5E32/jn95Z6z37yd/zClmJb+Jh4eHpBIxmUoyUrHo57yRywi3R825ouZ8omL3GCaZ+km47tZwrmg4TC33qmGqSz6KmA0EiiZ2I6lkHHqJlh0PBft0nMGwaXALGfTrvttGuhU3u2Z+aJ/45tt/NNd39Ne3ke+Dpi/WJEocsWBYJpWofgAYyUiYyWN12zg9+ATTzO42T3Qphz5W/NQxd4NZjkBctXE7lEPieRmiWoCfkf/7DK1HuKwLhF5rNaotBrXVoHWYEAeqdaBwDaGxwLhe78C8y+7V+94jfT7msFOOEu0w6rg989nUWVAom/zwSP4Hy9wELJHYJbIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Stage 1-3: a human interacts directly with the agent harness, which together with the model forms the agent&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/a7f1a6cd282b4cc29c89b221c4f01fb3/160a3/harness-stage-1-3.png&quot;
        srcset=&quot;/static/a7f1a6cd282b4cc29c89b221c4f01fb3/ff46a/harness-stage-1-3.png 325w,
/static/a7f1a6cd282b4cc29c89b221c4f01fb3/a6d36/harness-stage-1-3.png 650w,
/static/a7f1a6cd282b4cc29c89b221c4f01fb3/160a3/harness-stage-1-3.png 682w&quot;
        sizes=&quot;(max-width: 682px) 100vw, 682px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;At Stage 4+, the human&apos;s hands move one layer out. You interact with the &lt;strong&gt;work harness&lt;/strong&gt;: you trigger a versioned workflow, the work harness drives the agent through guides and sensors, and what comes back has already survived the feedback loop. The whole stack - work harness wrapped around the agent - becomes what we call a &lt;strong&gt;work engine&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;figure-portrait&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 778px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 131.69230769230768%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAaCAIAAAA44esqAAAACXBIWXMAAAsTAAALEwEAmpwYAAAD3UlEQVR42o1U609bZRg//4CJiTHGf2Jf3BezZDFz2wcnahZjNpf4Qc20wmCKIhsoSLcx17RIldmxrfQGlJbeabl1xbX2Si+0rEhvdI45Sun9cnra03OOT9uBRMfsyfMm57383t9z+b0PQlRK1TJKkXg4sOawWRLxrWR8K7H9JJva8bqd8A+7cOaZhsDAsSJFUYvzs709Pb+wh7+82NH9bZfp/tLAD/3iCRGaz5A4diAYDLbzmSTQppNxqUQ8o1aSeBlD88+h/QdcMxyjKCK+HXOp2KtqZjgUgGkVKzYFrjlfLScSyQn2ZRGjPRqN1sBNMldKBYgcgnz5lVdfePEl+kA/TPEmmRvgwWtXT5483nLqrfa2NoqqNstcL1jlz4cbCvm0UMDzeT1Qv4PBaAOM7oEh5+VSQaWU371zOxJa/0/M6DOZYRWtMcMoZNQqBW+MGwnWwE3GXAfjGFbMKeUyAZ+3EQ42Cd7HnM9o1MpxoTAU+GMfGH1+zGgjZgwtaGc00ilxJByiIGP/xwywIhiOFeDwRjgg4I9xuXdB6lDmA8BP+WpukwQOHpJkFeq8+ShqWtIH/F6zaQmmJEkQeJmoOwVCKBVy+x4JilBEOboRNlutTo/HueJbXVsXiaXXbrDcPr/D6bZYbdl0AnIBlksn53VayMoeHiHQzJDE0KXd+nra/9ktQyvP9gXX3Dpm/vz2b92qCE20YrS5QGo15RMV/cJ8cicGWmrUHyGKaZbU2K3P0XjLpwfEn96xn2PpzjI071+RdMrCbVNBk9ML/lfqz8agX0gltuGup8wQrUTCv9RDYw59zx6hM4d6WcN9LHbf0M/9Vwe7er+7sDg/s/3kMSDhtOHeYuyvzdROrJBNgfNIPl92zfVQgZZNQ4tLfGxVcWJV8eYD+TGn+I2S+3TW8bZmmmM2W4EN+Od02qnJCaGA71q2wwpSKOK/yzox41Eb78g4/ZB97PX7nMOGm4e1w69tzZ14qDqyoOEBmKygoJ+FWZ1Wowblg/5BiwhUQyrjXhr4mMHuYI10MDmdrNFvmCMXGex2OqP1Mv283WYcFwpGb3GgPYHsoU+hhSyO1USJkPn84OLkJ0Fla0hzfk1xZu7Xdyd/PKsfpYXUF6Laj5ZFEzql224r5FKQ7SWDHgKGNDUUjVDFYp+M897cjXP64Q9mmcdvfnX0Ou0Uv/eM/qcP77HfkV3hjHPtJlOjq6gU8nQiDrfUSwXgajkYCRg9VqvPYfE5HOseR9Br87ssKw6L12H1LfsfeGOPH0F6oL36Vtz/flUkUcvk7kfu2u6crJJ1hdbaK4HvvUKiUvob1HI3KI0HnbsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Stage 4+: a human interacts with the work harness, which wraps the agent harness and model; together they form the work engine&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/91ce2b22cbea4a031373b47af58c40ec/20982/harness-stage-4plus.png&quot;
        srcset=&quot;/static/91ce2b22cbea4a031373b47af58c40ec/ff46a/harness-stage-4plus.png 325w,
/static/91ce2b22cbea4a031373b47af58c40ec/a6d36/harness-stage-4plus.png 650w,
/static/91ce2b22cbea4a031373b47af58c40ec/20982/harness-stage-4plus.png 778w&quot;
        sizes=&quot;(max-width: 778px) 100vw, 778px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Work engine&quot; is also our coinage - but not from thin air. LangChain&apos;s own framing is that harness engineering exists to turn models into &quot;work engines.&quot; We are extending their phrase one layer up: the model becomes a work engine only when &lt;em&gt;your&lt;/em&gt; harness, not just the vendor&apos;s, wraps it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The shift in one sentence: at Stage 1-3 your prompt is the workflow; at Stage 4+ the workflow is versioned in the repo and you trigger it. That is not a model upgrade. It is a change of interface - and of what you spend your day improving.&lt;/p&gt;
&lt;h2 id=&quot;walking-the-delivery-cluster&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#walking-the-delivery-cluster&quot; aria-label=&quot;walking the delivery cluster permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Walking the Delivery Cluster&lt;/h2&gt;
&lt;p&gt;The two-harness vocabulary makes the three Delivery dimensions of the Harness Model concrete.&lt;/p&gt;
&lt;p&gt;If you have not read that post: the model spans five stages, from No AI Process through Chatbot-Assisted and Human-in-the-Loop to Systematic Harness and Agentic Flywheel. Across the Delivery cluster, the early stages mean that whenever a human works with AI at all, their hands are on the agent harness - prompt, watch, correct - while at Stage 4+ they move to the work harness and trigger versioned workflows.&lt;/p&gt;
&lt;h3 id=&quot;human-agent-interaction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#human-agent-interaction&quot; aria-label=&quot;human agent interaction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Human-Agent Interaction&lt;/h3&gt;
&lt;p&gt;Kief Morris&apos;s &lt;a href=&quot;https://martinfowler.com/articles/exploring-gen-ai/humans-and-agents.html&quot;&gt;loop progression&lt;/a&gt; - outside the loop, in the loop, on the loop, flywheel - restates cleanly: &quot;on the loop&quot; literally means your hands move from the agent harness to the work harness. And the reason you cannot skip ahead is documented. Böckeler&apos;s &lt;a href=&quot;https://martinfowler.com/articles/exploring-gen-ai/13-role-of-developer-skills.html&quot;&gt;catalogue of coding-agent steering&lt;/a&gt; found that even in successful sessions, &quot;intervention, correction and steering happened all the time&quot; - what she calls &quot;supervised agent&quot; mode, exactly the Stage 1-3 territory of our model. Until your guides and sensors absorb that steering, a human has to supply it live, on the agent harness.&lt;/p&gt;
&lt;h3 id=&quot;workflow--process&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#workflow--process&quot; aria-label=&quot;workflow  process permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Workflow &amp;#x26; Process&lt;/h3&gt;
&lt;p&gt;Maturing here means workflow definitions migrate from ad-hoc prompts into the work harness. Stripe is the Stage 4+ exhibit: &quot;A typical minion run starts in a Slack message and ends in a pull request which passes CI and is ready for human review, with no interaction in between&quot; - over a thousand merged PRs per week, with deterministic lint and test steps interleaved into the agent loop so required checks always run. OpenAI&apos;s &lt;a href=&quot;https://openai.com/index/harness-engineering/&quot;&gt;harness engineering post&lt;/a&gt; compresses the same operating model into four words - &quot;Humans steer. Agents execute.&quot; - and notes they have &quot;pushed almost all review effort towards being handled agent-to-agent.&quot; Review itself became a sensor inside the work harness.&lt;/p&gt;
&lt;h3 id=&quot;reliability--operations&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reliability--operations&quot; aria-label=&quot;reliability  operations permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reliability &amp;#x26; Operations&lt;/h3&gt;
&lt;p&gt;Unattended operation is only trustworthy if the work harness carries state and verification, because the agent harness alone cannot. Anthropic&apos;s &lt;a href=&quot;https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents&quot;&gt;long-running agent pattern&lt;/a&gt; is the machinery: a specialized &lt;strong&gt;initializer agent&lt;/strong&gt; sets up an &lt;code class=&quot;language-text&quot;&gt;init.sh&lt;/code&gt;, a &lt;code class=&quot;language-text&quot;&gt;claude-progress.txt&lt;/code&gt; log, and an initial git commit; every later session reads the progress file and git history, verifies the app still runs, and commits incremental work. Their motivating finding - &quot;compaction isn&apos;t sufficient&quot; - is the reliability argument in miniature: durable state lives in the filesystem and git, which are work-harness artifacts, not in the agent harness&apos;s context tricks. This is the same investment as the runbook and alert-handling axes in the matrix: agents earn operational trust through structure, not optimism.&lt;/p&gt;
&lt;h2 id=&quot;its-engineering-not-model-magic&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#its-engineering-not-model-magic&quot; aria-label=&quot;its engineering not model magic permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;It&apos;s Engineering, Not Model Magic&lt;/h2&gt;
&lt;p&gt;If this sounds like a lot of unglamorous systems work - it is, and the skeptical evidence says that is precisely the point. A &lt;a href=&quot;https://arxiv.org/abs/2604.00189&quot;&gt;study of 138 practitioner talks&lt;/a&gt; on deployed agent systems concludes: &quot;allocate most of your resources to integrating tools and APIs, designing memory and workflow layers, implementing orchestration, and setting up observability and debugging. These engineering tasks dominate development time more than model behavior.&quot; (Fair caveat: that is the authors&apos; qualitative takeaway from self-selected talks, not a measured-hours figure.)&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.swarmia.com/blog/dora-2025-report-ai-readiness/&quot;&gt;2025 DORA report&lt;/a&gt; lands the organizational version of the same finding. It warns that existing delivery bottlenecks will &quot;neutralize any gains from AI&quot;, because &quot;Every line of AI-generated code still needs to go through your review process, your test suite, and your deployment pipeline.&quot; The teams positioned to capture the value are the ones that treat internal platforms as real products with dedicated ownership. Pipelines, tests, fast feedback, governance - DORA does not say &quot;work harness,&quot; but it is describing one.&lt;/p&gt;
&lt;p&gt;Our conclusion from both: the agent harness is necessary, and the vendors are racing each other to build it for you. The work harness is what gates the value, and nobody is going to build it for you.&lt;/p&gt;
&lt;h2 id=&quot;start-building-your-work-harness&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#start-building-your-work-harness&quot; aria-label=&quot;start building your work harness permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Start Building Your Work Harness&lt;/h2&gt;
&lt;p&gt;The vocabulary in one breath: three layers (model, agent harness, work harness), two owners (the vendor builds the inner layers, your team builds the outer), one loop (steer the agent by iterating on guides and sensors). And one marker of maturity: which harness your humans touch all day.&lt;/p&gt;
&lt;p&gt;Two things to do this week:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Audit the interface.&lt;/strong&gt; Watch where your team&apos;s hands actually are. Mostly prompting, watching, and correcting in a terminal? You are on the agent harness - pre-Stage-4 territory, and that is fine, as long as it is a deliberate stop and not a permanent address.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Convert one recurring mistake.&lt;/strong&gt; Pick the agent error your team has corrected at least twice this month and turn it into a guide (a rule, a skill) or a sensor (a linter, a check in the loop). That single move is the steering loop in practice.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And if you are earlier in the journey - no agent in your daily work yet, nothing to audit - start with our &lt;a href=&quot;https://handsonarchitects.com/blog/2026/how-to-start-with-ai-assisted-development-today/&quot;&gt;getting-started guide&lt;/a&gt;. The two moves above will still be here when you are ready.&lt;/p&gt;
&lt;p&gt;We are collecting patterns across teams for the Q2 2026 update of the &lt;a href=&quot;https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/&quot;&gt;Harness Model&lt;/a&gt; - if your team has moved a workflow from prompts into a work harness (or tried and bounced off), we would like to compare notes. Email us at &lt;a href=&quot;mailto:contact@handsonarchitects.com?subject=Work%20Harness%20%E2%80%94%20compare%20notes&quot;&gt;contact@handsonarchitects.com&lt;/a&gt; or DM either of us on LinkedIn: &lt;a href=&quot;https://www.linkedin.com/in/maciej-laskowski&quot;&gt;Maciej&lt;/a&gt; · &lt;a href=&quot;https://www.linkedin.com/in/tomaszmichalak&quot;&gt;Tomasz&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;summary-box&quot;&gt;
&lt;h2 id=&quot;tldr&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tldr&quot; aria-label=&quot;tldr permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;&quot;Harness&quot; is one word for two different systems. The &lt;strong&gt;agent harness&lt;/strong&gt; is the control layer that ships with the product - orchestration, tools, sandboxes - and the vendors are racing each other to build it; whatever edge it gives you, your competitors rent at the same price. The &lt;strong&gt;work harness&lt;/strong&gt; is the layer your team builds on top: the conventions agents read before they act, the linters, tests, and review agents that check what comes out, and the versioned workflows that string them together. Nobody will build that layer for you - it encodes your module boundaries, your compliance constraints, your definition of done - and it is the part you own and the part that compounds.&lt;/p&gt;
&lt;p&gt;The budget translation: most of the published excitement is about the layer you buy, but the returns are gated by the layer you build. The empirical evidence points the same way - across deployed agent systems, integration, workflow, and observability engineering dominates development time more than model behavior, and DORA warns that existing delivery bottlenecks will &quot;neutralize any gains from AI&quot;: every AI-generated line still passes through your review process, your test suite, and your deployment pipeline. Funding the work harness largely means funding those.&lt;/p&gt;
&lt;p&gt;A walk-the-floor diagnostic for your next review: watch where your engineers&apos; hands are. If the day is spent prompting, watching, and correcting agents in a terminal, the prompt &lt;em&gt;is&lt;/em&gt; the workflow and it evaporates with the session - fine as a stage, expensive as a permanent address. The maturity marker is engineers triggering versioned workflows whose output has already survived automated checks before a human looks at it. That jump is not a model upgrade you can purchase. It is an interface change you have to engineer.&lt;/p&gt;
&lt;p&gt;Two questions to ask your leads this month: &lt;em&gt;which harness do your hands touch all day?&lt;/em&gt; and &lt;em&gt;which recurring agent mistake did we convert into a guide or a sensor?&lt;/em&gt; Then place your team on the Delivery rows of &lt;a href=&quot;https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/#delivery-interaction-workflow-reliability--operations&quot;&gt;our maturity matrix&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Team Topologies in the AI Era: Where Does the Cognitive Load Go?]]></title><description><![CDATA[At the Pragmatic Summit, Martin Fowler asked the question every technology leader reorganizing around AI is circling: "Are we seeing two…]]></description><link>https://handsonarchitects.com/blog/2026/team-topologies-ai-harness-cognitive-load/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2026/team-topologies-ai-harness-cognitive-load/</guid><pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsTAAALEwEAmpwYAAABtklEQVR42gGrAVT+APDr45uEd5mHev38+ObZvtrAifj28LOwqJaRiOjm3//78vr27sTEwY6Tk/Tz7dTe3u3s5pCmq4ipteDl4QCehXaFTymykXT8/f3TxqGpiTvz9PKlk4CMZ0ZaW1f////g39sqNDhBSU29u7avsq7Szsc2Sk8OKjbKz88AuKaVk21Qmnpi5t7T5+HXytPV+PDmsIlnqXtXspeA6Ofg7enhwsTBoKalGzM8Fy0zf5CTm6eqT1tfXGpuAN65mmMtEVEvJbGTdbyGStzBotSmdqKTg2JaToCyx8XAt7Wwpdbd3ZKhpl1lZFdeXEpibHCKl2lvbFFRTwC+UQPJWQutTxC7dkPNt6DbxbCLhXUVhr8VdqQhc5c9fJzOycJ3jphrf4ipsa3OyLs5U2I2XHFrcnJiVk0AsWQq6JBMvHpEtnZAyphnzqR1fW5PYXNml4FhgmlOSXmNmpuZNUFFkZKPzse6k5CIRFRbcIaQVmVoXWZnANyRROOXSeOWR9SPRqtoJKhkHLBqI8yBOPKcROWWRemaSMyUV5qShdTV0dvXz11lZkxUV6Wtrvb06//78fln9HYYdyvdAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Architects eating pizza and robot that don&amp;#39;t eat pizza while juggling several keyboards and code editors.&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/dc28e89e8ad8fc6d01ea1da17363d359/3c492/hero-image.png&quot;
        srcset=&quot;/static/dc28e89e8ad8fc6d01ea1da17363d359/ff46a/hero-image.png 325w,
/static/dc28e89e8ad8fc6d01ea1da17363d359/a6d36/hero-image.png 650w,
/static/dc28e89e8ad8fc6d01ea1da17363d359/3c492/hero-image.png 1300w,
/static/dc28e89e8ad8fc6d01ea1da17363d359/d544a/hero-image.png 1950w,
/static/dc28e89e8ad8fc6d01ea1da17363d359/6976b/hero-image.png 2172w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;callout-note&quot;&gt;
💡 Running an engineering org rather than a team? Jump to the &lt;a href=&quot;#tldr&quot;&gt;TL;DR section&lt;/a&gt; for the executive summary.
&lt;/div&gt;
&lt;p&gt;At the Pragmatic Summit, &lt;a href=&quot;https://newsletter.pragmaticengineer.com/p/cycles-of-disruption-in-the-tech&quot;&gt;Martin Fowler asked&lt;/a&gt; the question every technology leader reorganizing around AI is circling: &lt;em&gt;&quot;Are we seeing two-pizza teams becoming one-pizza teams because agents don&apos;t eat pizza, or do we see two-pizza teams staying and becoming much more effective and capable? My bet is on more effective two-pizza teams.&quot;&lt;/em&gt; It sounds like a question about headcount (since &lt;a href=&quot;https://martinfowler.com/bliki/TwoPizzaTeam.html&quot;&gt;two-pizza team&lt;/a&gt; is 5-8 people, then one-pizza team would be 3-4?). We think it is really a question about &lt;strong&gt;where the cognitive load goes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Team Topologies&lt;/em&gt; is usually read as a book about org design. Its engine is something narrower and more useful: &lt;strong&gt;cognitive load as a finite, per-team budget&lt;/strong&gt;. Every &lt;a href=&quot;https://teamtopologies.com/key-concepts&quot;&gt;team type the book defines&lt;/a&gt; is a different way to keep one team&apos;s budget from overflowing. An AI harness is a new lever on exactly that budget - so in this post we re-read all four team types through one question: when the harness moves the cheap load, where does the expensive load end up?&lt;/p&gt;
&lt;h2 id=&quot;an-ai-harness-is-a-cognitive-load-lever---which-is-what-platform-teams-do&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#an-ai-harness-is-a-cognitive-load-lever---which-is-what-platform-teams-do&quot; aria-label=&quot;an ai harness is a cognitive load lever   which is what platform teams do permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;An AI harness is a cognitive-load lever - which is what platform teams do&lt;/h2&gt;
&lt;p&gt;Terms first, because &quot;harness&quot; is a contested word. We use it the way Birgitta Böckeler does in &lt;a href=&quot;https://martinfowler.com/articles/harness-engineering.html&quot;&gt;Harness Engineering&lt;/a&gt;, and the way our &lt;a href=&quot;https://handsonarchitects.com/blog/2026/architectural-governance-at-platform-scale/&quot;&gt;governance post&lt;/a&gt; already does: a harness is everything in an AI agent except the model itself.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Agent = Model + Harness&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The harness is the context the agent reads, the tools it can call, the guardrails and linters and tests that check its output, and the feedback loops that improve all of those over time.&lt;/p&gt;
&lt;p&gt;Now the Team Topologies side. The book defines &lt;a href=&quot;https://teamtopologies.com/key-concepts&quot;&gt;exactly four team types&lt;/a&gt; - &lt;strong&gt;stream-aligned&lt;/strong&gt;, &lt;strong&gt;enabling&lt;/strong&gt;, &lt;strong&gt;complicated-subsystem&lt;/strong&gt;, and &lt;strong&gt;platform&lt;/strong&gt; - and exactly three interaction modes: collaboration, X-as-a-Service, and facilitating. We built on this foundation before, in &lt;a href=&quot;https://handsonarchitects.com/blog/2025/services-architecture-and-code-ownership/&quot;&gt;Services Architecture and Code Ownership&lt;/a&gt;. The part that gets lost in re-tellings is &lt;em&gt;why&lt;/em&gt; the types exist: a team&apos;s cognitive capacity is finite, and when the domain it owns exceeds that capacity, delivery slows, quality drops, and people burn out. The other three types exist to absorb load &lt;em&gt;off&lt;/em&gt; the stream-aligned team, so it can stay focused on its stream.&lt;/p&gt;
&lt;p&gt;Read the harness&apos;s job description against that. It absorbs boilerplate, glue code, codebase search, and first-draft implementation off a stream-aligned team. That is the exact job description of a platform team. This is not just our analogy: the platform engineering community &lt;a href=&quot;https://platformengineering.org/blog/ai-and-platform-engineering&quot;&gt;explicitly describes its own discipline as cognitive-load mitigation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Which raises the tempting move: should the team that builds the org&apos;s harness be crowned a new, 4.5th team type? Team Topologies is emphatic that there are four types and &lt;strong&gt;no hybrids&lt;/strong&gt; - and we think it is right. The &quot;what type is it?&quot; question is the wrong one. Ask instead: &lt;em&gt;whose load does it reduce?&lt;/em&gt; A team that builds and operates the harness - context templates, agent workflows, eval loops, guardrails - is a platform team whose product happens to be the harness. It is consumed the way Team Topologies says platforms should be consumed: &lt;strong&gt;X-as-a-Service&lt;/strong&gt;, behind a clear interface (&lt;a href=&quot;https://handsonarchitects.com/blog/2026/what-makes-mcp-server-successful/&quot;&gt;a well-designed MCP server&lt;/a&gt; is exactly that interface - and, as we return to at the end, perhaps a Conway&apos;s Law force of its own), with governance baked into the harness rather than enforced in review - &lt;a href=&quot;https://handsonarchitects.com/blog/2026/protecting-architecture-with-automated-tests-in-python/&quot;&gt;architecture tests that fail the build&lt;/a&gt; are the canonical example.&lt;/p&gt;
&lt;p&gt;Here is the whole argument in one picture. Watch the return arrows - every load the harness platform takes off the stream team sends something back.&lt;/p&gt;
&lt;div class=&quot;diagram-desktop diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.38461538461539%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsTAAALEwEAmpwYAAABrklEQVR42m2S627aQBCF/f7vUDVSHqBqf4CUC5SmdUVwwObiAgV7WWzvGof6Ft/2Mhs7raqqyejoaP+c0bczo6lXBQDTEF3vlzfucpMQBappyuY8KYNhEQxzfFMEoyK4hWyu/Y38I2Unfs+x+o71PdzhIhaslPFdtLoMpu/o/IKY78/rS0h0rRGKScWlYqLzp5pzBrszHTq2cXJv98tveCtBQE2gwVAfoTm2rjqnmu3L0SIamuHApJ+tUN/kp1RWsrZCPA3QjCBaJS9/UVwokN1DSiV4R6utqRpv06EVtmrzhltHKcRVptPdwyO68zb77NRmy6J2p7/u++6458yujuRHLqXU4gLOT78lo0yQmGdl13ZBj7r38yva6Ghbsqaphb/MVyNiXB3sLzTclEKA9mrUkrdMALuETojzQNGMIiZbYsUyyTNoxVLJctlhw3+DfunAhWip/ixBKQHSZ9mRxR5PfJG2jllMWa6pt2qVBddoOTjYA2yP/HVQpeMc9QLz42HyAd1/wkaPWFbla28eiRtHc4LXj8Q++XP/EBX5rPKMGuuZ0/fMcYEmFV7UwTMEGykw87m4oQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Horizontal flow diagram of the four Team Topologies team types read as cognitive-load flows: the stream-aligned team relocates boilerplate, glue, and first drafts to a harness platform team and learning load to a time-boxed enabling team; tacit specialist load stays with the complicated-subsystem team; verification, integration, and judgment flow back into the stream-aligned team&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/fb0facf4e1dbb27a13ca79f78faf2fbb/3c492/team-topologies-load-flows.png&quot;
        srcset=&quot;/static/fb0facf4e1dbb27a13ca79f78faf2fbb/ff46a/team-topologies-load-flows.png 325w,
/static/fb0facf4e1dbb27a13ca79f78faf2fbb/a6d36/team-topologies-load-flows.png 650w,
/static/fb0facf4e1dbb27a13ca79f78faf2fbb/3c492/team-topologies-load-flows.png 1300w,
/static/fb0facf4e1dbb27a13ca79f78faf2fbb/bb543/team-topologies-load-flows.png 1418w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-desktop diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.38461538461539%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAACgElEQVR42n1Sz0sUcRQfTTtUSInsKivtrruu27gz4+7Mzuzs/N7d+e2uywy7um66IuIhCKp/YCgK8tghqlOINw9FBJGB4CkqrfAiSKBRh0LpVIeotW8zihCIfeDzfbz3eO993uMLQUfR4j0wDJ8cyvFPaVPbptTC50GWvOfFny04HSLeuyimQxs5KrymMP3vJCq0KpGhTQ7tugsdg/2m8SyxSCg5QChSM8FRH+LZ9LU7jtbBoL7VsoyCMZNo2toQGDXw5kg+AXjUtwTBytVOX6LoP90n+w5JFmfCAIBWpqhdyQ4b61iOXebKw+9pU18RBPiMgvtkFjlXk5KdlpjstD3LIh01g/RLUCQzskwotR/08NRO1pjapc3JnUHe/nVBuFhGufNhQs3N8+XifdpQ52MZfMZT7g5rcQTQBkEP2g954LsI4sZblLcAqdb3KLX+h9Ynm7FMCfTzE1a2JBAJIbsJM+QrXBY3YCY959Vcrju9FKxuKdkqMNjaz1zaAsmo9Hq/YSQ/Tfcxo3qEq6lRrq7A+YaGKg1bbTghL89ZpduUIa/wI8Ul0bYeGY1Kt9N4eDYR5rdoVAd50v6dQXQAB9k30P+wtuatAkFYXriOy9LzlCw+IfXCQkzAu7x4EZsZzEWqKbl/fEiJj+MlZHrgoLJSOXGEkNPqZlpnZ2dPOY7T9u8guFI5GRBxs1fBygEtYfjFuBnQET1QQK0eDlOO/TIu2qMk5iqTvrvqvqYK4hdMZHbjEmUFM8g6ms8AXOP2CEMAKZVtIq7fk4q9PHbliqvUvd0lyS7PkUbhBlM0b8rV6i1+YizZTQy88CORnSCZ+Ijp7KcAHt/2o9Fv3fjA478LNsi/8q2PDwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Horizontal flow diagram of the four Team Topologies team types read as cognitive-load flows: the stream-aligned team relocates boilerplate, glue, and first drafts to a harness platform team and learning load to a time-boxed enabling team; tacit specialist load stays with the complicated-subsystem team; verification, integration, and judgment flow back into the stream-aligned team&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/a41945529c39242ebde3f28a3a17d85a/3c492/team-topologies-load-flows-dark.png&quot;
        srcset=&quot;/static/a41945529c39242ebde3f28a3a17d85a/ff46a/team-topologies-load-flows-dark.png 325w,
/static/a41945529c39242ebde3f28a3a17d85a/a6d36/team-topologies-load-flows-dark.png 650w,
/static/a41945529c39242ebde3f28a3a17d85a/3c492/team-topologies-load-flows-dark.png 1300w,
/static/a41945529c39242ebde3f28a3a17d85a/bb543/team-topologies-load-flows-dark.png 1418w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-mobile diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 79.6923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAIAAACZeshMAAAACXBIWXMAAAsTAAALEwEAmpwYAAACE0lEQVR42oVTaW/aQBDl//+NVlX7reohUYUWSpqoCVUacwQoh9fGxsY2i8HXXrPbNaaBpFR9WvnDzLw53oxr6gRy/12nqmsTA2U/zfQeZYZVLLB89J6i9jd5tVWdeV7vuB+/2++uUHsQz8PS+R+yEFIIHqd8seYu5gvMHMytiK1iBtrBBTzNcCQzzhkXTJQgQK+9X5fuuBPMvTxuO6Pr1ezCfLj15xyE3OeQSh7IOqsQUBbnQptBSSeJpoGFsJvQ3dhDCDvTwLZwKPfuasJaNaouWwAzk2iCfZSGVNvIUkSXqd1I7QvqNanfktuOAuplO3MXTOOVk+BDZRAQZruWNfxqj+pTY02JKlDuvfd7L8Lhq1X/ZTR+LXBTibwXuq3FsGEO7jxUEwBajYwUhDEAKFWBsn/JE1k4kLuSLKFwpX7EV1JUlfTYZdv32K6bvYY1qM+7N6uZNjPG5Zm9HAA6Yp+9JEdZ4m6xt9u4MfbijSaVvWjZOac544XgBEo9Cg5QNvXIPK5KRz+rBgqCWfrQ9o2mM2h7TnfHiHh+JFp6Dd2qXh7IaoGqOobILEbfgh+fkPHFXfZTRkA9vdLjnk/7qSJ4IWkiyJ9XiXT+wgrK5lmEKLYoXrAYEYwYXvNM/RsnZOA3Gar7xlt0+2Zy9WFx93k9mJCwvEN57p86JVMQA+IPedDL3c7GHPGgTz2bbtR5Ykn+DbkbkwIcKSeyAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Vertical flow diagram of the four Team Topologies team types read as cognitive-load flows: the stream-aligned team relocates boilerplate, glue, and first drafts to a harness platform team and learning load to a time-boxed enabling team; tacit specialist load stays with the complicated-subsystem team; verification, integration, and judgment flow back into the stream-aligned team&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/e465406827df9602502dc92dd0cebc88/3c492/team-topologies-load-flows-mobile.png&quot;
        srcset=&quot;/static/e465406827df9602502dc92dd0cebc88/ff46a/team-topologies-load-flows-mobile.png 325w,
/static/e465406827df9602502dc92dd0cebc88/a6d36/team-topologies-load-flows-mobile.png 650w,
/static/e465406827df9602502dc92dd0cebc88/3c492/team-topologies-load-flows-mobile.png 1300w,
/static/e465406827df9602502dc92dd0cebc88/37048/team-topologies-load-flows-mobile.png 1352w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-mobile diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 79.6923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAADNElEQVR42pWUTUzTYBjH65gOiIqf4BgDdJt03fe6tfto+7ZrSzcKuE27DT9AF0UOhGQaJZhgMfHCgcNOSmKyGweOHnZkEBUPotGD4YIXjZ68ePBgwqztFEUBo8+hbZp/f+///T9PXwjaUrJOu9qYPNUVSb5wAumxm84uu+jMIziaemWJZq7XZJJUB/1T/RDaqEspO5FWqOTlauzMiMJlR9dR/pzSiafv/RdwdnZ2t6IoOjx7096luoHJ7ChMpK/BpDQKU9lxj5AXKxVZPyzLjYgk7VE/2bUTa1dhZqahUCg09KpiDYpE0XaUB8soT79005ElBGADAYF5hVDhtWCc++Dj6AWUQ5tkWdYBAPTa/SetWCwb5EpFrz2rsO/baWo66KbwMfZUYhLnyUlvyAnEXHqCiNPjXEocj2fO5DTQn87qIPWlCtG32C0OezQ4hSW4W3DYfxeytJpPYXtJwt28kiBsy4mo9TmPmRdjAfNT0nForhiHDG6WPu0g8SlvjLqNx2PnN6A1q36OFoICq26HXY2Iwscuhoz0Yk1JEu1QTgve9X7WqaS6PVWRcShhd8ubh7Kx0c+x9/0seI/3CG/xBD8HdXg6DqAAHJEGr5qSFy4cDklSw8qKojWmFvZgrM3E+40pIdDaz/ia+/lAa18MNSZ7sBamIoONiHQbegiOoEsoz3xFOfpToDtWdVKh10YUbSyVKvXQ5pB3nLL5OrUZv3KkpGSYG8j0gGRfr5DLiel8ntIimJ9XhaVSfbFcNAyKBRPjSFoIm3gi4ohbesJnO4aGhuo1kKbbdqViuWz4aXtzw9QKwuKDsCtRZbD0x6hH/Oyzsau52FjLxrj9YVnSLOu0QQXqirVhBZBe67zVajVoGq+VLWEOQeHDmS+kr09xdpLvJGLi6LbAX3NYNPyWxSZxLnKjlYRT/hAiYri9HwceySuDLdqtlZ+e3nciHhoyk+4RE+G60ozDw+3Ac8VIIMOmPp/7r462c2I9F99vCsBrLgZTgglQjaZ4xdcdXT9J+BRj2HFH0yASskf6h4OhBkSHexuPBbrmjiCdT9r8cMVG+haPOo8vNLssz8yk6+Lmn2Cn+gYH6QIkon3fAgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Vertical flow diagram of the four Team Topologies team types read as cognitive-load flows: the stream-aligned team relocates boilerplate, glue, and first drafts to a harness platform team and learning load to a time-boxed enabling team; tacit specialist load stays with the complicated-subsystem team; verification, integration, and judgment flow back into the stream-aligned team&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/87c8e56f9d297f65952f778ef9e4f667/3c492/team-topologies-load-flows-mobile-dark.png&quot;
        srcset=&quot;/static/87c8e56f9d297f65952f778ef9e4f667/ff46a/team-topologies-load-flows-mobile-dark.png 325w,
/static/87c8e56f9d297f65952f778ef9e4f667/a6d36/team-topologies-load-flows-mobile-dark.png 650w,
/static/87c8e56f9d297f65952f778ef9e4f667/3c492/team-topologies-load-flows-mobile-dark.png 1300w,
/static/87c8e56f9d297f65952f778ef9e4f667/37048/team-topologies-load-flows-mobile-dark.png 1352w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;stream-aligned-the-product-engineer-question&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stream-aligned-the-product-engineer-question&quot; aria-label=&quot;stream aligned the product engineer question permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Stream-aligned: the Product Engineer question&lt;/h2&gt;
&lt;p&gt;The stream-aligned team is Team Topologies&apos; default: five to nine people owning a slice of the product end to end. It is also the team type with the most at stake in the AI era, because it is the team the harness was built &lt;em&gt;for&lt;/em&gt;. A wave of practitioner writing has already renamed its members - the &lt;strong&gt;Product Engineer&lt;/strong&gt;, a &lt;a href=&quot;https://martinfowler.com/articles/expert-generalist.html&quot;&gt;generalist&lt;/a&gt; who, amplified by agents, spans design to code to operations.&lt;/p&gt;
&lt;p&gt;So which way does Fowler&apos;s question break? Does the team shrink to fewer generalists - or hold its headcount and own more scope? Both forks are live, and we are not going to pretend the data settles it.&lt;/p&gt;
&lt;p&gt;What we &lt;em&gt;can&lt;/em&gt; say is what the surviving work looks like. Kent Beck, on the same Summit stage, described his experience of pairing - two humans plus a genie - and praised the genie&apos;s slowness: &lt;em&gt;&quot;When the AI goes away for three minutes, we can talk about our philosophy of naming, or how we express conditionals, or about what we should be doing next.&quot;&lt;/em&gt; That three-minute gap is not idle time. It is where the irreducible work happens: deciding what to build, judging what came back. The genie generates; the humans verify and steer.&lt;/p&gt;
&lt;p&gt;Beck also supplies the brake on the strongest &quot;smaller teams&quot; reading: &lt;em&gt;&quot;AI is an amplifier.&quot;&lt;/em&gt; We made the same argument in &lt;a href=&quot;https://handsonarchitects.com/blog/2026/how-to-start-with-ai-assisted-development-today/&quot;&gt;How to Start using AI in Software Development&lt;/a&gt; - an amplifier does not subtract team members; it changes what each one carries. It also resolves a tension with &lt;a href=&quot;https://handsonarchitects.com/blog/2025/services-architecture-and-code-ownership/&quot;&gt;Services Architecture and Code Ownership&lt;/a&gt;, where we argued that a stream team should own its services full-lifecycle: a leaner team does not break that argument, it concentrates it. The verification load is the new weight of ownership.&lt;/p&gt;
&lt;p&gt;One more data point, from inside a 4,000-engineer AI-forward organization: Jamie Hurst &lt;a href=&quot;https://jamiehurst.co.uk/2026-05-24_ai-sustainable&quot;&gt;reports&lt;/a&gt; that the compression runs &lt;em&gt;upward&lt;/em&gt;. &lt;em&gt;&quot;The work that used to need a team now sometimes needs one person with the right tools, and that person tends to be senior because seniority is where the system-level understanding lives.&quot;&lt;/em&gt; The generalist who absorbs the team is senior - and Hurst is blunt that it is not free: &quot;the role isn&apos;t sustainable at this pace.&quot; A field report of one, but worth sitting with.&lt;/p&gt;
&lt;h2 id=&quot;enabling-consultants-who-teach-calibrated-trust-then-leave&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#enabling-consultants-who-teach-calibrated-trust-then-leave&quot; aria-label=&quot;enabling consultants who teach calibrated trust then leave permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Enabling: consultants who teach calibrated trust, then leave&lt;/h2&gt;
&lt;p&gt;The Team Topologies authors&apos; &lt;a href=&quot;https://teamtopologies.com/news-blogs-newsletters/2025/2/21/team-topologies-interaction-modes-breaking-through-common-misconceptions&quot;&gt;2025 guidance on interaction modes&lt;/a&gt; sharpened something the book always implied: collaboration is temporary by design, and facilitating is &lt;strong&gt;self-terminating&lt;/strong&gt; - entry and exit criteria are part of the mode. That maps one-to-one onto a model we expect to see everywhere: a small group of consultants - internal or external - who introduce the harness to a stream team, then leave.&lt;/p&gt;
&lt;p&gt;The scope matters. The job is not installing the harness; the job is teaching the team to carry the &lt;strong&gt;verification load&lt;/strong&gt; the harness creates. &quot;Calibrated trust&quot; sounds soft, so make it concrete:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;When to override the agent.&lt;/strong&gt; Anywhere the work touches domain rules no sensor in the harness checks - correctness is outside the harness&apos;s remit when nobody specified it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How to tell the harness itself is wrong.&lt;/strong&gt; If the guardrails never fire, treat that as a smell, not a triumph: either quality is exceptional or detection is inadequate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;What honest verification looks like.&lt;/strong&gt; &quot;We merged it because the tests are green&quot; is not verification when the same agent generated the tests.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The exit criterion writes itself: the enabling team leaves when the stream team can answer those three without them in the room.&lt;/p&gt;
&lt;p&gt;Hurst&apos;s field report shows why this work needs an explicit owner. In his organization, mentoring and 1-2-1s were &lt;em&gt;&quot;the first thing to go&quot;&lt;/em&gt; - precisely because they are the load AI does not absorb. Unless someone is accountable for transferring judgment, it silently disappears from everyone&apos;s calendar. The cheapest defense we know: write the engagement&apos;s exit date, exit criteria, and a named owner into the kickoff doc on day one - that single line keeps the facilitating mode self-terminating and the judgment transfer on someone&apos;s accountability list. We argued the upskilling half of this years ago in &lt;a href=&quot;https://handsonarchitects.com/blog/2023/just-in-time-knowledge-sharing/&quot;&gt;Just-in-time Knowledge Sharing&lt;/a&gt;; what that post stopped short of, and Team Topologies supplies, is the time-box - the exit date.&lt;/p&gt;
&lt;h2 id=&quot;complicated-subsystem-the-ai-resistant-boundary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#complicated-subsystem-the-ai-resistant-boundary&quot; aria-label=&quot;complicated subsystem the ai resistant boundary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Complicated-subsystem: the AI-resistant boundary&lt;/h2&gt;
&lt;p&gt;If you had asked us in 2024 which team type AI would dissolve first, we would have guessed this one: specialists, absorbed by models trained on everything. The early evidence points the other way. A &lt;a href=&quot;https://dl.acm.org/doi/full/10.1145/3772318.3791176&quot;&gt;CHI 2026 study&lt;/a&gt; found AI assistance is weakest exactly where tacit, specialist knowledge matters - the settings where you cannot easily verify the output because the expertise lives in people&apos;s heads. One study, n=60, so treat it as directional rather than definitive. But it matches a nuance &lt;a href=&quot;https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/#foundation-context-and-team&quot;&gt;our own maturity matrix&lt;/a&gt; already states: agents provide &lt;em&gt;technical&lt;/em&gt; depth on demand - frameworks, languages, patterns - not &lt;em&gt;domain&lt;/em&gt; depth.&lt;/p&gt;
&lt;p&gt;Re-read the complicated-subsystem team through the load lens. It exists so stream-aligned teams do not have to carry deep specialist load - the video codec, the pricing engine, the ML internals. The harness relocates &lt;em&gt;generalist&lt;/em&gt; load; it cannot relocate &lt;em&gt;tacit-specialist&lt;/em&gt; load, because tacit knowledge is precisely the kind you cannot write into a context file. So of the four types, this team&apos;s load budget is the &lt;strong&gt;least changed&lt;/strong&gt; by AI. Everyone else&apos;s work got cheaper to generate; theirs did not.&lt;/p&gt;
&lt;p&gt;That inverts the intuitive prediction: the complicated-subsystem team is not the first boundary to dissolve - it is the most durable one. There is a sharper, forward-looking version of this idea (if AI is weakest at tacit knowledge, is tacit knowledge now a reason to &lt;em&gt;draw&lt;/em&gt; a boundary?), but it rests on that same single study, so we hold it for the open questions at the end.&lt;/p&gt;
&lt;h2 id=&quot;the-stage-34-jump-load-relocated-not-removed&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-stage-34-jump-load-relocated-not-removed&quot; aria-label=&quot;the stage 34 jump load relocated not removed permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Stage 3→4 jump: load relocated, not removed&lt;/h2&gt;
&lt;p&gt;Time to name what we have been planting all along. In &lt;a href=&quot;https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/#foundation-context-and-team&quot;&gt;the Harness Model&lt;/a&gt;, our maturity matrix for AI engineering, the &lt;strong&gt;Team (Humans + Agents)&lt;/strong&gt; dimension moves from Stage 3 - &lt;em&gt;&quot;Leaner delivery-minded team; agents use file/terminal&quot;&lt;/em&gt; - to Stage 4 - &lt;em&gt;&quot;Small generalist team per initiative; agents run full stack.&quot;&lt;/em&gt; The matrix states the shape of the jump. This post is about its mechanism and its price: &lt;strong&gt;The harness does not delete cognitive load. It relocates it.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Generation gets cheap; verification, integration, and judgment get expensive. The CHI study gives the receipt: verification load is measurable and &lt;em&gt;mediates fatigue&lt;/em&gt; - more checking means more tiredness, even while raw output climbs. The Stage 3→4 jump is that trade executed deliberately: the team can get leaner only because the platformizable load left - and it stays viable only if it can pay for the load that came back. Here is the stream team&apos;s ledger across the jump:&lt;/p&gt;
&lt;div class=&quot;diagram-desktop diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.769230769230774%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB8klEQVR42o2SW2+bQBCF+f9/pVIrpXXj2gh8weBgfK8TNTV2JNfE+MZtl2UvMxT80FR5aVdHR/vy6YzOjFb+4+HNpMyWp50dBcPXFyvY9I+7AeYrrSBBHD6nJ5/FL5VnFx8hf8NurgTjmZM/tUj7fv/xA2ndS19H5mpZNFyGg1lgLw6D2WG4P5pY7PENLqUCKRhQV+06YmbyuSFnJuy7mHtaRqc2+WGcH/Vw2Uv9bTKCfC9ViQgVDwAVXAIvqYuxBccunvuVQ9RHMtJU5tG4y7JBkQ1oavGkU8oQ6sA6WUJZsYLnIhuyxzZtNKNPd6zxTfkGMk9LyXQULyfXhXedT5KnbeJC8QqAPFqS14fiNAIeISogLpx6ctOR247aduDSr8dOyNSJFvZhbIfj4WXpx65igVLAzvPrtkcCG3hcxaujpXyzIuXGrLU24OxoQDyeWpIOK3HiqLRbihD/jK1QqWp4lq9N1tLjuy/kvpl8bhS6DjtLI9nETp/ax1XrMDeT9Uv8oOivvwpTdWEoIHbgVLeFF+vWWQeSB61I3U3irmPvZ+Q+J+Nr3Ct5UL5blWLi5IiJzsZ67rXyUVvMDbg4Gopjyfc1IILqg5WAvj8SLIqgy80mNZpUbxD9K++3IOxp/3eeAuh3SCaYTjGbVq6SMdLVbzRXk2dkiKVQAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Ledger diagram of the stream-aligned team&amp;#39;s cognitive load across the Stage 3 to 4 jump: code generation, boilerplate and glue, and first-draft tests leave the team through the AI harness, while verification, integration, judgment and direction, and context engineering arrive as the new expensive load&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/cb2dbbe2aae4c8f509b0e43c502323a8/3c492/stream-team-load-ledger.png&quot;
        srcset=&quot;/static/cb2dbbe2aae4c8f509b0e43c502323a8/ff46a/stream-team-load-ledger.png 325w,
/static/cb2dbbe2aae4c8f509b0e43c502323a8/a6d36/stream-team-load-ledger.png 650w,
/static/cb2dbbe2aae4c8f509b0e43c502323a8/3c492/stream-team-load-ledger.png 1300w,
/static/cb2dbbe2aae4c8f509b0e43c502323a8/0f586/stream-team-load-ledger.png 1498w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-desktop diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 58.769230769230774%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACrUlEQVR42pWTS2sTURTHpyoKPptMMmnaySRz78x9ZZ6dvJqmTdNirUoXIsWF4sZXVYL4CYr4HVzowu+hC11aXx9BJDuxiFRBbOo93qkP7ELQC+f+OZsf58//HE379zeSfuPjR3SO8k87NfayGeH1btN7PjcVrE8n7DWt6I+1RoAvnurW+4vt6Honcm6caCer3Ya47ZYP8z9Bv7Tb8E3fLXya5mWY50yeaU9Bj1HZcIsgsL6h+Xxi4Lc4hG1PBi0BXksMowRBFecup4AVTdv7J3CmVisxpH9M8Bg0UGm75VjfGsjcjpEB3M6+11zXeGsmBNxOtM1mJ7+V6tWvJCzDJCteTQFCiP1K9iz1lw4AwEg5f3CMlTObAdYhQnkZVHSIsVInnwI3NI5yA48VgDo5qQo4MYYBNYCYxy6lwLU12JdqCku1NxWXq46x2aYW9AiVp+MYeg6RdTQGFCkgIYUBqlEgNSZJnYMdu0MSlZVlY8fyUsu+e3KWPVxsVh6c71FqWVaGo+znyDEgwUVZc8YhQUUZ4NyPCV0FxE0OXieU1ekA3KZQwAr4rHglBS7P4Dvnl5P7Z4+ze+cWbGKaZjZEuc91XAQF2wGmmvYByinLtj6okjwQlJWqgGJ96Ke99dPyk92Wk2lhNYm5OeM4cCIM5bwQcNwPZAdjNamxoVHHeGMlFOhMvMXnkqHV8L7QsAKTYuJ3KGsqlH6/vxOKbRwqVO3sZqwsB7YuVe1o2nvphB7JD6hKlcZI8hiDG9lD3zehamd+rM3K7rVJEmGFuPCh7ViyQ9FWh+HhLMdbTVyC0M6904g1ekFYozdF6eg1Wjq6KqxjV3lp9NbfFtv3HTOhE5+6BMGcYHKhKmDeE7Lj2pA4yvL/nh5Tp8cqmScq1RdeOfNM1XqqAdJf8Yr+6DuxRvGPf0+WlwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Ledger diagram of the stream-aligned team&amp;#39;s cognitive load across the Stage 3 to 4 jump: code generation, boilerplate and glue, and first-draft tests leave the team through the AI harness, while verification, integration, judgment and direction, and context engineering arrive as the new expensive load&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/64984c5f78b060432369c1c3d99a00af/3c492/stream-team-load-ledger-dark.png&quot;
        srcset=&quot;/static/64984c5f78b060432369c1c3d99a00af/ff46a/stream-team-load-ledger-dark.png 325w,
/static/64984c5f78b060432369c1c3d99a00af/a6d36/stream-team-load-ledger-dark.png 650w,
/static/64984c5f78b060432369c1c3d99a00af/3c492/stream-team-load-ledger-dark.png 1300w,
/static/64984c5f78b060432369c1c3d99a00af/0f586/stream-team-load-ledger-dark.png 1498w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-mobile diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 876px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 196%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAnCAIAAACABf9ZAAAACXBIWXMAAAsTAAALEwEAmpwYAAAE2ElEQVR42oWWh3LbOBCG8/4vcndzGdeoS7ZEEiQl2eqSZTty3BPbEgsINpTlLeU4F8eN8w9mReDjouwu9CnLMojPgI1VOM3i8fVXbT6sn8+bp9PG6aTh33VOp/uLI+3bsaHYBMIphNjOQXEEPwFkKhxmgZb42sTv2A8H9l3n0Ol1Hw46992+P+jetOiPerJqQmCspavABpX8gidZYDFX74YLPbjavh5unPe3LodblwMS3fajuQhIFrYBmVyWCg5Apb/DJPaNntezvAlZDUyUM7Ccge1Ph25XUoLMk0wVdJ95BorzsYVP2FJTlGSB+VNoUzSs7AOY2YlDpG9F1GKBHbN2xNqPLYpSM11/5U3PkWOgq7bX3/dPKneD2sOovhyj9leTBj059+yM4YLfgOMcNmyKR/Tt8/nBxlV/+2a4cdkv3I4b0dWZ18mC9+HAunDNs+Bw4fe+Bb2LsH/OehesP/c6K9/8AFbUwrlJzxCOnjHcLWMtHY/jcQvhfRiYlbgk9cjctee0dxr0USdB/2vQm/uHty5+pfvsnP/wnLpm7OokPGm4C4yZzcvB7s1492ZkRLcT7yCj7d/hMVAdz3ANmxkzuWskCNMRCU5b7qzpTLFtOVMzXMxcOwuewVPIY8iOVwTjNmM2ehaeyXziYxtYjmNQNKjleSTxceRTeOZZJWOQDGQYR56SYZYlacoEDzHdMhWh0sjD3gylIjRARhn6fIR/PVJBFHOlVJpyLgS+EVIlqeRCZa89z2Ccg3AO/Yu6e16jl3v8XguXc1h3vA+vu0UETtNdbNxP//4x/iu82gZ/oOBj+Oe04+BeRj94eKeSh9i/SROWpCLGTeAC3oFxqUnKcQQAoJHPQyhc81MvcCH/hFVyDXn1mnM6FWwWu+OTaevsmCxvDhfHxvmpdXqkz0eNs6OWf38g6UixEUSzvJIl158UWrSFYcw9nbu6ojq72/NuG9LTjodbs8EGG+y6zZ3AKvikIPtV0a2IWQlYU0VHCM8xNjEwuEdSjFtmA2snrolFK3aI/70Bw31JDlc7BfdLZbldFFpHduvANBUev4QtDG+M07zQMTvG9BqUuF6NtWLcKsWtAtcqfICejQ9gidHurXPQMwCjEuUR5eiAL6muondhcA16s5/c6xnmqWeqlQF5wtq5PoIJJ1+SWo2VC1GlENcrvFwUJ1VI2vkn3oWJooYg1bShPXzeuPvns/elKGtNcVx/F8Z98q14mRczcVQRo5qaNWC2hy3vleTtPkT5pfEGHLUhbceUpFixRAdEW3GUjUYmuhDaudvg1TX7prraE+OqnO/5/aqc1dVRY609MamJ48rTpfEKjHcCEaOy0uzV5na+zp3CanMH7eXGVtrQpVVVAXkDxopJTTEqynojLBboznZcLoWlYlQq4k9eq3OjAHnpfQGvbzkr8fKbTT1o4JDMMQEP1iG5VoZ60NVKf33a64vXjJeteKUBM/9XPk+S7zAqtN+Ao7YYl4Re8RqFWCulRiXRS6le4kaVG2V5VccB631+CfsGxLboVWWr4+yWV1tFZ7dy9+/2crPgFWpgtMWiAjHC5gs4nIHfhNDks6KwS6JbFp0yJi1ft2jzdlFe14CR3Ad9FNpNFWI+J5eAxSGcqnAkae9VQTRZ/wn6pSmwISSX/wGPZT6cG6ekNQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Vertical ledger diagram of the stream-aligned team&amp;#39;s cognitive load across the Stage 3 to 4 jump: code generation, boilerplate and glue, and first-draft tests leave the team through the AI harness, while verification, integration, judgment and direction, and context engineering arrive as the new expensive load&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/85feef4403b692fde804544669f2d026/1b1d5/stream-team-load-ledger-mobile.png&quot;
        srcset=&quot;/static/85feef4403b692fde804544669f2d026/ff46a/stream-team-load-ledger-mobile.png 325w,
/static/85feef4403b692fde804544669f2d026/a6d36/stream-team-load-ledger-mobile.png 650w,
/static/85feef4403b692fde804544669f2d026/1b1d5/stream-team-load-ledger-mobile.png 876w&quot;
        sizes=&quot;(max-width: 876px) 100vw, 876px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-mobile diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 876px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 196%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAnCAYAAAAPZ2gOAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGVUlEQVR42o1WS29bRRR222Da0pT42o7ta/u+328/Ezt2ncRO4qRNW4oqIaSKBfwCJBASEtmDEHTBIqtKVBVVJEofKg/ROoVFQajLrtiwQEj8AhQ3vhzOXNtpU5LUV/o0596Z+803cx4zoRA+MhP5TOeiv2gcdU9jTnZajWLn/Olmp92sdM60G53V5UZnyhM6q+3ZTrtV6yzNVTo6H+vg+PuInxUm+jVNj0dDw8cQog9cOQa2OtmTiwKoFRN0hFRSQZnSQJ02QCjK4NZtKDks5HQabClG4Dv4nylG/85mo/QOocZT9y0hCpae7CYdoZfNGz1vsdYrLDd63lK9l0OkHLVHO1zPkKI9jYv0UATBNpKBzlN/Mczx1A6hylMPLOwwlMkeX5CARWUiqhKn9AACgi0oIBZFMFEZEg3hE0JcNio89lShyiIhDsSZeqYUB8/IgEl+4KkdkPdd30YldJQUWGIcdCUOmjIJCv6giLEAKk5GoKsJMLBFYiSMHUQY7bkaHShhSzJkywaojTzIdQ+kmhtArNpAlzVQXAYMNvIiQlSo0kD2M4N7mS1bkF9pgLtUB2uhCs5SDcz5aZjMqyCjt81RCcn+qC4Lgp1FMMCZGWANGngzjXYapBwHup4Cg6NGJURPshPg4F56ShIs5lVwuAg4LMEE2Phu4bvJoUJ00EiEaINNyORJkPM8CKiYQ8UEPFGNrehxvu5kQWcjuwN7X0I5idmThGReAqHigo37GOxnswJ2qwoJV/HFgkCWvpsQf35gyyRgqb6XxcAGBxU6GCJMWYFsQQVh2gSxYg9aC+ic7CuYqobwvEIu8qNBApSn/nHU1JbBR7fwW9eSE11TjHd1ZbKrqYmuIse7pkFvqUrf1rXEli7H/8WxfzJM7GnqiXQkyyZP6HQsrFQqxRzDUMZ4OCRbluJoQsaKhjE7x8NKejwk1/L5gpyksNiEFS4aVtloWMtkTkpIMxba6wGAQ4jDxN54+PDYxuPH4Wf6DpP+0AjPoWcQWq6kPz7T0H56vWV9d25ev9Msp7+/tOq9RfouXgwdeW78zn97kaKKxljdiT9qVRU427S2zy+427MlDlbKsTXS32jss7R91B1a24Sxjc8vMZ++bUhr75SUD95khC/eX9F//Xbt5Pr6o+MbGxsnrmxuHt1H5W6lOPjIlc0/jg4n+uTLH14hxvo6vEQmGo5rNNbGLl/+/eU95amZyHsaF/1RZSI3LDl5xxAn77ha+ubZ5ebt5YXa3ZmSeWtlsX57cX7m7tnluZtvnGvffK09+03Fle56cvyWI8RuGVz0hsZS95Vs5F1S/q+7WPuwUPZIELuYGbYch6LFQNkVwMHUa89NwQpmxrwtwKKtw5whwbypQlVIQ03MQl1NbzvIoQvUNQxq6iu7n3ZPMG99zF9f5yM+nhuBrXETQVu2Wb+UTfgmnfWXCkW/5eWwLflOhvWrXLpLMg1T+CrJ4+sDwh4pBKQg6EGJj8GwlJF3B5WfkjJQExiYU8UA85oEdYEl37dJLcCqc+2FhIGNZcxSU8FZ4mCeByfkAC7pE2IBoTIKIVFoo10yWayNpKhiHeTxGxKTo0LvY9selZAcB/MSA6dEERZ0HZqaCrOyBHM8BxWZBrV/Ao5KmAqU1PgslHkRzk9Nw4XqDCx6OShnGJiWkFAYgZDsFe4LkNpIllyT01AVaSiziQBTHDpIzkBBSoA2CqHGR3DmCNgantFKAhS0VRHPDoQqRQNbxjH64AaBx+/+hISghDNXpTQuMwk1XYQpIQVlLrGDCqqtyvTOleQFTklAHclcOgOni0W4UKnCouvB6UIxwJliCScSoM5n0GGxgwltOREoPEX2jGXRqxosWxbMKTK0NG0HNZ4PPE/uNQcSmhIqHARwELAYbyZHBe0QNt8PZnJmH7xkvO+RQM2jZ73gfkPtgkVADjJshziQEKtOr45LrfMMtEwdZlUBc5SDhsxjIPNQFzFnMY/LeFY/S/Z/wmG1EagnWI58O836C7mCv5gv+k0v769WZvx2sezPOp5vp9J+VUn5SOjjanxjACTsEg7kuhrC/bruDerhKTWDDmGhLjPo6T7Ie03CmodtAzGNcWpiLA7u2AHQ29vesB4qDPURlp3fcOn30DGbnhTbBazIfRtbB1scuxfuIR7J2YkP/wOrIqIfmWU8JgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Vertical ledger diagram of the stream-aligned team&amp;#39;s cognitive load across the Stage 3 to 4 jump: code generation, boilerplate and glue, and first-draft tests leave the team through the AI harness, while verification, integration, judgment and direction, and context engineering arrive as the new expensive load&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/7c23575004b07f6e94c4ef387e2aa429/1b1d5/stream-team-load-ledger-mobile-dark.png&quot;
        srcset=&quot;/static/7c23575004b07f6e94c4ef387e2aa429/ff46a/stream-team-load-ledger-mobile-dark.png 325w,
/static/7c23575004b07f6e94c4ef387e2aa429/a6d36/stream-team-load-ledger-mobile-dark.png 650w,
/static/7c23575004b07f6e94c4ef387e2aa429/1b1d5/stream-team-load-ledger-mobile-dark.png 876w&quot;
        sizes=&quot;(max-width: 876px) 100vw, 876px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;To make the ledger less abstract, picture a five-person stream team a quarter into the jump - a composite sketch, not a case study. Nobody on it hand-writes boilerplate, glue code, or first-draft tests anymore; the agents do. The hours that work used to fill now go to reviewing agent output before it merges, making the integration calls the agent cannot make, keeping the context files current enough that next week&apos;s drafts stay trustworthy, and learning. Same five people, same forty hours - every line item moved.&lt;/p&gt;
&lt;p&gt;The matrix already says this in its own voice: &lt;em&gt;&quot;Teams that try to shrink without paying either cost end up with the same number of humans and less agent trust.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And here is where we push on our own model. The matrix commits to &quot;small generalist team&quot;; Fowler bets on more-effective same-size teams. We now think the next revision should assert less and explain more: &lt;strong&gt;hold the headcount open, assert the load relocation.&lt;/strong&gt; The relocation is what the evidence supports; the headcount is downstream of where the relocated load lands, and that differs by team and by domain. The Q1 matrix is explicitly a snapshot built to be revised - consider this our first input to its next version.&lt;/p&gt;
&lt;p&gt;Hurst compresses the whole thesis into one sentence: &lt;em&gt;&quot;The cost of building has collapsed, but the cost of aligning organisationally has not.&quot;&lt;/em&gt; One practitioner&apos;s report - but it is exactly what relocated load looks like from the inside.&lt;/p&gt;
&lt;h2 id=&quot;the-software-house-angle-the-harness-is-the-ip&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-software-house-angle-the-harness-is-the-ip&quot; aria-label=&quot;the software house angle the harness is the ip permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Software House angle: the harness is the IP&lt;/h2&gt;
&lt;p&gt;A deliberate gear-change here, from org design to economics - and if you are not in the agency business, stay anyway: everything below applies equally to any organization running many product teams on a similar stack. For a Software House, the relocation thesis has a commercial consequence. Relocated cognitive load does not evaporate - when relocation is done well, it gets &lt;em&gt;captured&lt;/em&gt;: in context templates, eval loops, guardrails, agent workflows. Captured relocation is durable and reusable. The model is rented; the harness is owned.&lt;/p&gt;
&lt;p&gt;The mechanism is well supported: &lt;strong&gt;harness design, not the model, drives outcomes&lt;/strong&gt;. One &lt;a href=&quot;https://huggingface.co/blog/agent-glossary&quot;&gt;benchmark cited by Hugging Face&lt;/a&gt; moved from 61.5% to 87.2% on the &lt;em&gt;same model&lt;/em&gt; through harness design alone. Our own &lt;a href=&quot;https://handsonarchitects.com/blog/2026/harness-engineering-for-e2e-tests/&quot;&gt;E2E test harness&lt;/a&gt; is the worked proof at small scale: sharper specs and a better harness cut commits per test step roughly 5× while CI time stayed flat - with the leverage in the spec, not the model.&lt;/p&gt;
&lt;p&gt;The inference we draw from that - and it is our extrapolation, not a finding - is that a Software House accumulating proven harness templates starts every project ahead: faster delivery, more consistent quality, and an asset that survives model churn because a good harness re-targets to the next model. Before anyone turns that into a sales deck, two brakes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Experience precedes platform.&lt;/strong&gt; You cannot harness-platformize patterns you have not lived. The harness platform should be extracted from a few real engagements - thinnest-viable, demand-pulled, exactly the rule Team Topologies applies to every platform. Building it speculatively is premature optimization with a roadmap.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ROI scales with stack homogeneity.&lt;/strong&gt; Harness templates behave like service templates: cheap to reuse across twenty similar Spring Boot services, expensive across twenty different stacks. A heterogeneous portfolio pays more to platformize and gets less back.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The economics are moving this way regardless. Hurst again: a piece of internal platform that previously served 4,000 humans &lt;em&gt;&quot;now potentially serves 4,000 humans plus an unbounded number of agent instances those humans deploy.&quot;&lt;/em&gt; The industry is starting to call this &lt;strong&gt;agent experience&lt;/strong&gt;, and it is moving platform investment from team-level to board-level priority. A harness platform team is that investment with a name and a team type.&lt;/p&gt;
&lt;h2 id=&quot;what-we-cant-settle-yet&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-we-cant-settle-yet&quot; aria-label=&quot;what we cant settle yet permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What we can&apos;t settle yet&lt;/h2&gt;
&lt;p&gt;Three questions we are deliberately leaving open - not because they are unanswerable, but because answering them today would be guessing:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The headcount fork.&lt;/strong&gt; Do stream-aligned teams shrink, or hold headcount and own more scope? Fowler bets on more-capable two-pizza teams; our matrix currently bets on leaner ones.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conway&apos;s Law with agents.&lt;/strong&gt; If an agent is a communicating participant in building the system, does Conway&apos;s Law now describe human↔agent and agent↔agent communication - and is choosing a harness a &lt;strong&gt;reverse-Conway move&lt;/strong&gt;, shaping architecture by shaping the agent topology? The matrix&apos;s &quot;agents are first-class team members&quot; principle is the on-ramp; nothing stronger than practitioner speculation exists yet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tacit knowledge as a fracture plane.&lt;/strong&gt; If AI is weakest at tacit knowledge, does tacit knowledge become a place to &lt;em&gt;draw&lt;/em&gt; team boundaries, not just defend them? We think so. Nobody has tested it, and the supporting evidence is one n=60 study.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All three are inputs to the next, post-Q1 version of &lt;a href=&quot;https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/&quot;&gt;the Harness Model&lt;/a&gt;. This post is meant to shape that model, not just apply it.&lt;/p&gt;
&lt;p&gt;If you want to do something with this tomorrow: run each of your teams through the one question this post runs on - &lt;em&gt;whose cognitive load does this team reduce, and what load flows back?&lt;/em&gt; Then self-assess on the Team dimension of the matrix. And if your answers disagree with ours - especially on the headcount fork - argue with us: email &lt;a href=&quot;mailto:contact@handsonarchitects.com?subject=Team%20Topologies%20in%20the%20AI%20Era&quot;&gt;contact@handsonarchitects.com&lt;/a&gt; or DM either of us on LinkedIn: &lt;a href=&quot;https://www.linkedin.com/in/maciej-laskowski&quot;&gt;Maciej&lt;/a&gt; · &lt;a href=&quot;https://www.linkedin.com/in/tomaszmichalak&quot;&gt;Tomasz&lt;/a&gt;. The best version of this argument is the one we have not heard yet.&lt;/p&gt;
&lt;div class=&quot;summary-box&quot;&gt;
&lt;h2 id=&quot;tldr&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tldr&quot; aria-label=&quot;tldr permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;AI coding agents do not delete engineering work - they relocate it. Writing code gets cheap; checking it, integrating it, and deciding what to build stay human and become the expensive part. Any plan built on &quot;AI means fewer engineers&quot; without asking where that expensive work lands is budgeting from half the ledger.&lt;/p&gt;
&lt;p&gt;On headcount, the honest answer is that nobody has reliable numbers yet - even the best study keeps correcting itself. Martin Fowler bets teams stay the same size and become more capable; our own maturity model bets on leaner teams. Both outcomes are live. What is certain either way: each remaining person carries more reviewing and judgment per head, so budget for that load, not just for seats.&lt;/p&gt;
&lt;p&gt;The tooling around the AI - the &lt;em&gt;harness&lt;/em&gt;: the context, guardrails, and workflows wrapped around the rented model - is where the results actually come from, not the model itself. Treat the team that builds it as a real product team, not a side project. For a Software House this is the commercial headline: everyone rents the same models at the same price, but the harness is owned, and it compounds - every new project starts ahead. Two cautions before that becomes a sales deck: build it out of a few real projects rather than speculation, and expect the return to scale with how uniform your tech stacks are.&lt;/p&gt;
&lt;p&gt;One counter-intuitive staffing signal: your deep specialists - the people whose value is hard-won knowledge that lives in their heads, not in documents - are the &lt;em&gt;least&lt;/em&gt; exposed to AI, not the most. AI is weakest exactly where unwritten expertise matters. Plan around keeping them.&lt;/p&gt;
&lt;p&gt;The single question worth running every team through at your next org review: &lt;em&gt;whose workload does this team reduce - and what work flows back when it does?&lt;/em&gt; Then place yourself on the Team dimension of &lt;a href=&quot;https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/#foundation-context-and-team&quot;&gt;our maturity matrix&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[When the Weakest Dimension Wins: Architectural Governance at Scale]]></title><description><![CDATA[Thoughtworks puts the task-vs-system gap as cleanly as anyone has: "It's like buying a Ferrari and driving it on muddy roads: the engine is…]]></description><link>https://handsonarchitects.com/blog/2026/architectural-governance-at-platform-scale/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2026/architectural-governance-at-platform-scale/</guid><pubDate>Wed, 10 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAADJElEQVR42gEZA+b8ANjW1cHAwNbW1NXU0v/9+NHQ0Kysr7i4vrO1uLW1tsDBwcHBwb+9vr+/vsC/v8DAvr2/vb+/v7u8vLe4uQDFxMSSlJijo6a8vL328u7DwsPIzcjb2cLt6N7q6efu6+jv6+jq5OLr5d7t5d3t59/m5uHo5uLm5eC0trYA1NXSrrSxtbm3vsG/ubu6tbS3rMCxx8Kj8Ni82uLg7Ojk8/Dt9vTw+PLn+e7a+fHl9/Lu9fDu9/PwuLm5ANjh2LbLvL7QwrjMvfHx68zLy73CxLm0uObUz+Lm5/Ht6vPt6fPq5vfr4vjy7fj18vb08ff59fn7+Li6uwDZ4deux7W3zr3a4tj/+/fGxsbY1tPn5eLt6+rc2dze3drp5OH06uf37eb37N718+ju4d3iwLvjwbyzr68A5uXi1NXT19bU49/e+/byys3P5ebn//z48e3r4dzg7+zp9/Tw/fbt//jt+PPr9PXs6tjT1pSM0oyEsamoAOrn5dfV1N/d2+nm4/78+a+ekpR3ZOfg2vX08OPj3+7r6Pn28d3b2eHc1fn05/Lx6fHw7OHLx+/m4re7vADt6+rY2Nno5eLv6+j7/f2Wc1qZYjXW0M37+vbc39fx7er+/fx8c2oXGxva1Mf//u3x7unLxsXx7uu+v78A6+nn3dzb9fDt9PLw08vFtHVKiE4jgnFgVWJqWWJpXWRqi4B2gWdQLzg6SFllY211d4CGmJWXmJeah42RAPLp4uje2Pjz7+Pq7pNZM9RQAK9NC15BKAMZJQgWIQEKFiMyOQ1VfBdLZwgdLQIUJAQXJQ0iMgoeKhElLgD48/D28e7/+/bHyspZOSGmWiNnPR4UHSQKHyQoMjiZjoVFUlsGLkcMFyNPQCM2TzMJGiYMJBkEFSECEyAA5enfztLN7+vpwsLAUWFjanJ6X2hwV2FoW2NqanF4qaSfhoWGYWRpVV1lbG1gaHBfX2pyXWhpY2x0X2dwAPLw6+zq5vLv7Pr39O3u5+3r6PDs6vLu7PLv7O/s6uTj4uro5/Dt6/Lv7Ovp6urp6Ozt5/by7/n18vv39KvNLv2wNEZHAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Architectural Governance concept&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/096e9270d87c3a37a45a5486fd018fbc/3c492/architectural-governance-hero.png&quot;
        srcset=&quot;/static/096e9270d87c3a37a45a5486fd018fbc/ff46a/architectural-governance-hero.png 325w,
/static/096e9270d87c3a37a45a5486fd018fbc/a6d36/architectural-governance-hero.png 650w,
/static/096e9270d87c3a37a45a5486fd018fbc/3c492/architectural-governance-hero.png 1300w,
/static/096e9270d87c3a37a45a5486fd018fbc/71c1d/architectural-governance-hero.png 1536w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;callout-note&quot;&gt;
💡 For a quick summary, jump to the &lt;a href=&quot;#tldr&quot;&gt;TL;DR section&lt;/a&gt;.
&lt;/div&gt;
&lt;p&gt;Thoughtworks puts the task-vs-system gap as cleanly as anyone has: &lt;em&gt;&quot;It&apos;s like buying a Ferrari and driving it on muddy roads: the engine is powerful, but your arrival time is determined by road conditions and traffic.&quot;&lt;/em&gt; That image - borrowed from their work on &lt;a href=&quot;https://martinfowler.com/articles/structured-prompt-driven/&quot;&gt;Structured-Prompt-Driven Development&lt;/a&gt; - is what every productivity debate about AI tooling keeps returning to. The engine has changed. The roads haven&apos;t.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://metr.org/blog/2026-02-24-uplift-update/&quot;&gt;METR&apos;s February 2026 productivity update&lt;/a&gt; makes the same point in numbers. Its late-2025 cohort - 57 developers, 800+ tasks - puts both returning and newly recruited developers within a few points of zero, with confidence intervals wide enough to cross it in both directions. The honest read is not that AI helps or doesn&apos;t help - it is that task-level metrics are the wrong instrument for system-level questions.&lt;/p&gt;
&lt;p&gt;Architectural governance is a system-level question. That is the layer this article is about - and the argument is that when individual teams reach &lt;a href=&quot;https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/#foundation-context-and-team&quot;&gt;Stage 3&lt;/a&gt; in context engineering and workflow, governance is the dimension that determines what the platform actually delivers.&lt;/p&gt;
&lt;h2 id=&quot;the-productivity-measurement-trap&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-productivity-measurement-trap&quot; aria-label=&quot;the productivity measurement trap permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The productivity measurement trap&lt;/h2&gt;
&lt;p&gt;Per-task speed and platform throughput are not the same thing. If we make one developer 20% faster at writing a controller, the controller ships sooner. If two teams independently build incompatible contracts around the same bounded context, the integration cost is migration-level work - and it does not appear in any task-level study because it is not a task. It is a structural collision between systems that each shipped on time. Later in this article we walk through one concrete case: two teams both using &quot;external integration server&quot; to mean different things, which turns a documentation gap into a production incident.&lt;/p&gt;
&lt;p&gt;This is why the METR caveat matters more than the central estimate. METR is measuring the right thing for the question it asks (does AI speed up individual tasks?), and the answer is &quot;probably, with caveats that are getting harder to control.&quot; But that answer does not transfer to platform throughput - it is a different kind of question. Platform throughput is bounded by how fast a multi-team system can converge on correct cross-team decisions - and that is governed by shared vocabulary, shared diagrams, shared decision records, and the discipline of keeping them current. None of which appear in a task-level randomized controlled trial (RCT).&lt;/p&gt;
&lt;p&gt;We will use the term &lt;strong&gt;harness&lt;/strong&gt; throughout the rest of this article in the sense Birgitta Böckeler develops in her &lt;a href=&quot;https://martinfowler.com/articles/harness-engineering.html&quot;&gt;&quot;Harness Engineering&quot;&lt;/a&gt; piece: a harness is everything in an AI agent except the model itself.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Agent = Model + Harness&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The harness is the context, the constraints, the verification - the system around the agent that turns raw model output into something a team can trust. Harness engineering is the practice of designing and improving that system, the same way you would design and improve a test suite or a CI pipeline.&lt;/p&gt;
&lt;p&gt;The rest of this article is about what happens when the harness is built per-team but the system being governed is cross-team.&lt;/p&gt;
&lt;h2 id=&quot;the-harness-model-and-the-multi-dimensional-nature-of-maturity&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-harness-model-and-the-multi-dimensional-nature-of-maturity&quot; aria-label=&quot;the harness model and the multi dimensional nature of maturity permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Harness Model and the multi-dimensional nature of maturity&lt;/h2&gt;
&lt;p&gt;The short version: the &lt;a href=&quot;https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/&quot;&gt;Harness Model Maturity Matrix&lt;/a&gt; scores ten dimensions of AI-engineering practice, each across five maturity stages, so a team can see where it actually stands rather than where it feels like it stands. Those ten dimensions group into four clusters: Foundation (context engineering, team), Governance (security, architectural governance), Delivery (human-agent interaction, workflow, reliability), and Outcomes &amp;#x26; Learning (verification, knowledge, planning). Each dimension has five stages: from &quot;no AI process&quot; (Stage 1) through &quot;chatbot-assisted&quot; (Stage 2), &quot;human-in-the-loop&quot; (Stage 3), and &quot;systematic harness&quot; (Stage 4) to the speculative &quot;agentic flywheel&quot; (Stage 5). The matrix is a diagnostic tool. It is not a certification, and Stage 5 is informed speculation, not a documented endpoint.&lt;/p&gt;
&lt;p&gt;The central claim of the model is structural: &lt;strong&gt;maturity does not average across dimensions; the weakest dimension dominates the whole.&lt;/strong&gt; A team at Stage 3 in context engineering paired with Stage 1 in verification does not operate at the mean. The higher stages still ship at their own velocity, but the lowest dimension accumulates structural debt that erodes the value of everything above it. Unlike Liebig&apos;s law of the minimum - where plant growth is capped by the scarcest nutrient with a hard physical bound - the weakest dimension here does not impose a zero-yield ceiling. The debt compounds until it dominates the outcome. The mechanism is value erosion over time, not an instantaneous cap. We lean on the Liebig framing because it captures the right intuition (the bottleneck is what governs), but the mechanism is more like compounding interest than a hard wall.&lt;/p&gt;
&lt;p&gt;A concrete cross-dimension example: a team at Stage 3 in workflow generates code rapidly with AI assistance. The same team at Stage 1 in architectural governance has no linters, no structural tests, no codified invariants - boundary checks happen in code review or not at all. The Stage 3 velocity is real. Pull requests open and close faster than they used to. What also happens is that agent-generated code violates structural boundaries faster than humans can catch them manually. Six months later, the team owns more code, but the architectural shape has drifted. The Stage 3 speed shipped Stage 1 entropy. The weakest dimension dominated the outcome.&lt;/p&gt;
&lt;p&gt;This is the lens we want to apply to a specific dimension and a specific class of platform.&lt;/p&gt;
&lt;h2 id=&quot;why-ai-platforms-amplify-the-governance-gap&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-ai-platforms-amplify-the-governance-gap&quot; aria-label=&quot;why ai platforms amplify the governance gap permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why AI platforms amplify the governance gap&lt;/h2&gt;
&lt;p&gt;We use an AI Platform as the running example because the governance tension is sharpest there. The reasons are structural, not particular to any organization. AI standards (Model Context Protocol servers, Skills) are changing constantly - what was a sensible abstraction in Q1 is a legacy compatibility layer by Q3. Business expectations are enormous and under continuous executive scrutiny. Market demand makes shipping speed non-negotiable, which amplifies the pressure to skip cross-team coordination. Multiple teams build simultaneously, each with strong local context and weak cross-team visibility.&lt;/p&gt;
&lt;p&gt;Under these conditions, the architectural governance dimension - shared domain language, cross-team contracts, structural invariants, decision records - gets neglected not from negligence but from structural reality. Cross-team coordination requires cross-team ownership, and on a platform without a dedicated architect role, no single team owns it. Each team optimizes its own velocity. The aggregate cost is invisible until contracts collide.&lt;/p&gt;
&lt;p&gt;A specific class of collision: &lt;strong&gt;domain vocabulary&lt;/strong&gt;. Imagine a platform that distinguishes between four types of Model Context Protocol server - an &lt;em&gt;Internal Platform Server&lt;/em&gt; (platform-owned, internal data access), an &lt;em&gt;External Integration Server&lt;/em&gt; (platform-owned, external tool access), a &lt;em&gt;Verified Community Server&lt;/em&gt; (third-party, audited), and a &lt;em&gt;Custom Configuration Server&lt;/em&gt; (admin-configured, unaudited). Before this vocabulary is documented, two teams use these terms interchangeably. The collision surfaces at the security layer: a permission granted to &quot;an external integration server&quot; means different things depending on which definition was in scope when the rule was written. The fix at the documentation level is a ubiquitous language entry. The fix at the production level is an incident.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;bounded context&lt;/strong&gt; is the boundary within which a particular vocabulary and model are valid - the unit at which a team owns its own naming. A &lt;strong&gt;domain ubiquitous language&lt;/strong&gt;, in the Domain-Driven Design sense, is the shared vocabulary developed by engineers and domain experts and tested continuously against working software. For LLMs, the vocabulary is the contract. If it is precise and consistent, the model maps intent to implementation reliably. If it is vague or inconsistent, the model guesses - and it guesses faster than the team can catch the guess.&lt;/p&gt;
&lt;p&gt;Unmesh Joshi makes this argument cleanly in &lt;a href=&quot;https://martinfowler.com/articles/what-is-code.html&quot;&gt;&quot;What Is Code?&quot;&lt;/a&gt;: code is a conceptual model of the problem domain, and vocabulary is how that model becomes visible. His framing of the risk: &lt;em&gt;&quot;The code may compile. It may even pass basic tests. But if the team does not understand the conceptual model behind those structures, the codebase has gained vocabulary without shared understanding.&quot;&lt;/em&gt; He calls the gap &quot;cognitive debt.&quot; LLMs amplify it because they generate plausible vocabulary at a rate that outpaces a team&apos;s ability to develop shared meaning.&lt;/p&gt;
&lt;h2 id=&quot;the-knowledge-base---making-the-platform-model-machine-readable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-knowledge-base---making-the-platform-model-machine-readable&quot; aria-label=&quot;the knowledge base   making the platform model machine readable permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The knowledge base - making the platform model machine-readable&lt;/h2&gt;
&lt;p&gt;The first move in lifting architectural governance is making the platform-wide model machine-readable. The goal is concrete: when an engineer asks an agent &quot;how does feature X interact with team B&apos;s component?&quot;, the answer comes back with structural context - bounded contexts named, ownership identified, contracts referenced, prior decisions cited. Not from memory. From the knowledge base.&lt;/p&gt;
&lt;p&gt;That knowledge base has four components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;shared domain model&lt;/strong&gt;: bounded contexts with their ubiquitous language entries&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logical component-to-team mapping&lt;/strong&gt;: who owns what&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Architectural diagrams&lt;/strong&gt; that expose cross-team flows and invariants&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Architecture Decision Records (ADRs)&lt;/strong&gt; that make implicit decisions legible&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We will say more about the last two because they are the parts most often treated as documentation hygiene rather than first-class infrastructure.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;C4 model&lt;/strong&gt; (Context, Container, Component, Code) is a framework for visualizing software architecture at four levels of abstraction. The Level 0 view - the &lt;strong&gt;System Landscape diagram&lt;/strong&gt; - is the one that proves its value at platform scale. It puts every team&apos;s system on the same canvas. It shows cross-system relationships. Before a System Landscape exists, teams have no shared picture of the full platform to disagree with concretely. Disagreements happen in generalities. After it exists, principals from different teams can point at the same box and say &quot;this is owned by my team, here is the contract&quot; - or dispute the assignment. That is the point at which governance becomes possible: a common artifact that surfaces disagreements before they become migrations.&lt;/p&gt;
&lt;p&gt;&quot;&lt;strong&gt;Diagrams as code&lt;/strong&gt;&quot; is the second discipline. The diagram lives in version control, has a review process, and can be validated automatically - the same hygiene we apply to test suites. The diagram does not rot silently because automated checks (linting names, complexity thresholds, freshness timers) run on every change. The diagram is part of the build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ADRs&lt;/strong&gt; record the decisions: not just what was chosen, but the context, the alternatives considered, and the rationale for the chosen option. (We have written before about &lt;a href=&quot;https://handsonarchitects.com/blog/2025/using-generative-ai-as-architect-buddy-for-adrs/&quot;&gt;using generative AI as an architect buddy for ADRs&lt;/a&gt;.) For human teams, ADRs catch the &quot;why did we do it this way?&quot; question three months later. For LLMs, they do something more important: they make implicit decisions explicit context. A decision made in a Slack thread three months ago is invisible to an agent reasoning about a new feature. An ADR linked from the knowledge base is not.&lt;/p&gt;
&lt;p&gt;A wiki has all of these things and decays anyway. The reason the knowledge base described here does not decay is not a semantic distinction. It is mechanical - the ingestion harness in the next section. This is the responsibility Mark Richards frames in &lt;a href=&quot;https://developertoarchitect.com/lessons/lesson7.html&quot;&gt;Analyzing Architecture: Structural Decay&lt;/a&gt;: continually analyzing the architecture to keep it vital and to detect structural decay is part of the architect&apos;s job, not optional documentation hygiene.&lt;/p&gt;
&lt;h2 id=&quot;building-the-knowledge-base---the-ingestion-harness&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#building-the-knowledge-base---the-ingestion-harness&quot; aria-label=&quot;building the knowledge base   the ingestion harness permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Building the knowledge base - the ingestion harness&lt;/h2&gt;
&lt;p&gt;Maintaining cross-team documentation is hard for a known reason: teams fill templates under deadline pressure and produce consistency drift, silent gaps, and naming collisions that nobody flags until a contract breaks. On the platform we work on, we tried the template approach first. The output looked complete. It was not.&lt;/p&gt;
&lt;p&gt;What works is a &lt;strong&gt;conversation-driven ingestion harness&lt;/strong&gt;. The harness is an AI session that asks one focused question at a time, offers concrete options with their consequences, and refuses to accept a well-formatted document that has not been challenged. The pattern has three properties worth naming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conversation first, materialization second.&lt;/strong&gt; The session is interactive. Every question comes with an initial proposal to reduce friction and boost engagement. The AI does not generate the documentation as its primary output - it challenges the model until ambiguity is gone. When the session ends, the findings are materialized into a review artifact in a staging area, not written directly into the canonical knowledge base.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Separate review and apply steps.&lt;/strong&gt; The materialized session is a proposal. An explicit apply step, with per-item confirmation, is required before anything enters the canonical store. The human never has to fix AI-generated documentation inline - they approve or reject structured proposals. This is the &quot;human on the loop&quot; pattern Kief Morris describes in &lt;a href=&quot;https://martinfowler.com/articles/exploring-gen-ai/humans-and-agents.html&quot;&gt;&quot;Humans and Agents&quot;&lt;/a&gt;: &lt;em&gt;&quot;Rather than personally inspecting what the agents produce, we can make them better at producing it.&quot;&lt;/em&gt; The review step exists because the harness is not infallible. It is the last line of defense, not a formality.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gap discovery as the working signal.&lt;/strong&gt; The most useful feedback we have gotten from this process came from a platform principal engineer after one of the early sessions:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&quot;I answered one question in a tricky way, there was a hidden gap. A while later I was asked exactly the question I had in my mind.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That moment - the harness arriving at the gap the participant already suspected - is the credibility anchor. The AI is not generating good documentation. It is arriving at the gaps the participant already half-knew were there. To stay honest about what this means: the harness surfaces what neither party is blind to - it cannot catch a shared assumption both the contributor and the model hold. The early sessions are a promising initial result with motivated principals, not a claim validated at scale.&lt;/p&gt;
&lt;p&gt;The harness has evolved across iterations. The first version was a single advanced prompt - ~200 lines, embedding the full workflow, launched as a chat session. Value was immediate; the workflow was monolithic. The current version is split into small commands (start a session, apply approved findings, reject a session with reason) backed by a separate &lt;strong&gt;C4 model skill&lt;/strong&gt; that validates structural rules: naming linting, complexity analysis, and &lt;strong&gt;maturity checks&lt;/strong&gt; computed from git history. The maturity-policy timers flag a System Landscape diagram not updated in 12 months and a System Context diagram not updated in 6. The freshness signal is enforced automatically, not by convention.&lt;/p&gt;
&lt;p&gt;Re-ingestion is triggered not only by those timers but by any platform update, by new approved or developed work (there is a documentation-update loop when new work ships), and by a monthly Principal meeting where direction is discussed and sessions are orchestrated when needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The honest note about maintenance:&lt;/strong&gt; the harness makes ingestion tractable, not free. The ongoing cost is the human review cadence and the willingness to re-run sessions when the model changes. And freshness does not equal correctness - a diagram that is updated incorrectly generates false confidence. The harness reduces the cost of staying current; it does not eliminate it.&lt;/p&gt;
&lt;p&gt;What this section describes is harness engineering applied to the documentation pipeline itself. Each iteration improves the system that produces the output, not just the output. The C4 skill is a Guide (a feedforward control on what the diagram should look like). The maturity check is a Sensor (a feedback control on whether it is still current). Both are part of the same discipline.&lt;/p&gt;
&lt;h2 id=&quot;the-decision-harness---from-knowledge-base-to-governed-decisions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-decision-harness---from-knowledge-base-to-governed-decisions&quot; aria-label=&quot;the decision harness   from knowledge base to governed decisions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The decision harness - from knowledge base to governed decisions&lt;/h2&gt;
&lt;p&gt;The knowledge base is the foundation. The &lt;strong&gt;decision harness&lt;/strong&gt; is where it pays off. With a shared domain model in place, a new feature can be analyzed with awareness of cross-team contracts, complexity, and cost - before implementation is committed to.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A note on confidence:&lt;/strong&gt; the ingestion harness has been iterated through sessions with platform principals and produced the gap-discovery result above. The decision harness has not. It is at the plan and prototype stage - described from a design that has been worked through, not a production system that has run at scale. Treat the rest of this section as a designed pattern, not a validated result.&lt;/p&gt;
&lt;p&gt;The harness uses a canvas adapted from the &lt;a href=&quot;https://martinfowler.com/articles/structured-prompt-driven/&quot;&gt;Structured-Prompt-Driven Development (SPDD)&lt;/a&gt; method by Thoughtworks. SPDD treats prompts as first-class delivery artifacts and structures them with the &lt;strong&gt;REASONS&lt;/strong&gt; canvas (Requirements, Entities, Approach, Structure, Operations, Norms, Safeguards). The adaptation here is called &lt;strong&gt;REASINS&lt;/strong&gt;: we substitute the &lt;strong&gt;O&lt;/strong&gt; (Operations) for an &lt;strong&gt;I&lt;/strong&gt; (Integration), which changes both the canvas and its name.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Letter&lt;/th&gt;
&lt;th&gt;Section&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;R&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requirements&lt;/td&gt;
&lt;td&gt;What the feature must accomplish; outcomes, constraints, success criteria&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;E&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Entities&lt;/td&gt;
&lt;td&gt;Domain concepts in play; bounded contexts and ubiquitous language entries that apply&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Approach&lt;/td&gt;
&lt;td&gt;High-level direction; the option chosen among the alternatives considered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Structure&lt;/td&gt;
&lt;td&gt;Component-level shape; which components are affected, how they relate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;I&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Integration&lt;/td&gt;
&lt;td&gt;API contracts and dependency edges between components (replaces SPDD&apos;s Operations)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;N&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Norms&lt;/td&gt;
&lt;td&gt;Standards and conventions the solution must follow (naming, patterns, prior ADRs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Safeguards&lt;/td&gt;
&lt;td&gt;Explicit risk mitigations and failure-mode handling&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The Norms vs. Safeguards distinction is worth keeping in mind. Norms are what good looks like by default - the conventions that the solution should respect because the platform already runs that way. Safeguards are what the solution must do specifically to mitigate identified risks - the explicit &quot;if this fails, here is the fallback&quot; handling. Conflating them is how a canvas grows a section that looks complete but does not separate &quot;follow the platform pattern&quot; from &quot;this specific risk needs this specific mitigation.&quot;&lt;/p&gt;
&lt;p&gt;The reason for &lt;strong&gt;I (Integration) replacing O (Operations)&lt;/strong&gt;: SPDD&apos;s Operations section covers deployment, runtime behavior, and operational concerns. At the cross-team governance level, what we need instead is an explicit record of API contracts and dependency edges between components. The decision harness governs &lt;em&gt;what crosses team boundaries&lt;/em&gt;. Operations stays with the teams who own the components.&lt;/p&gt;
&lt;p&gt;To make this concrete, run the MCP-server permission feature through the canvas. Suppose a team wants to add a feature: &quot;let platform admins grant a scoped permission to an external integration server.&quot; This is exactly the feature that, left ungoverned, produced the collision earlier - &quot;external integration server&quot; meant one thing to the security team and another to the team writing the rule. Here is how REASINS forces the ambiguity into the open before any code is written:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Section&lt;/th&gt;
&lt;th&gt;Populated for the permission feature&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;R&lt;/strong&gt; Requirements&lt;/td&gt;
&lt;td&gt;Admins can grant a scoped, revocable permission to an external integration server; the grant is auditable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;E&lt;/strong&gt; Entities&lt;/td&gt;
&lt;td&gt;Pulls the bounded-context vocabulary: &lt;em&gt;External Integration Server&lt;/em&gt; (platform-owned, external tool access) - explicitly &lt;strong&gt;not&lt;/strong&gt; &lt;em&gt;Verified Community Server&lt;/em&gt; or &lt;em&gt;Custom Configuration Server&lt;/em&gt;, which carry different trust assumptions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A&lt;/strong&gt; Approach&lt;/td&gt;
&lt;td&gt;Permission resolved against the server&apos;s declared type at grant time, not at call time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S&lt;/strong&gt; Structure&lt;/td&gt;
&lt;td&gt;Admin console component and the permission-resolution component in the security context are affected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;I&lt;/strong&gt; Integration&lt;/td&gt;
&lt;td&gt;The contract edge that would otherwise collide: the permission API must take a typed server reference, not a free-text &quot;external integration server&quot; string the two teams read differently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;N&lt;/strong&gt; Norms&lt;/td&gt;
&lt;td&gt;Follows the platform&apos;s existing permission-naming convention and the prior ADR on server-type taxonomy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S&lt;/strong&gt; Safeguards&lt;/td&gt;
&lt;td&gt;If a server&apos;s declared type is missing or ambiguous, deny by default rather than fall back to the broadest scope&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The Entities row is where the bounded-context vocabulary stops being documentation and starts being a constraint: the canvas cannot advance with &quot;external integration server&quot; left as prose, because the entry has to resolve to one of the four named server types. The Integration row is where the collision is caught - it names the contract edge (a typed server reference) that the two teams would otherwise have implemented as a string each interpreted its own way. The documentation gap and the production incident are the same gap, surfaced before the code instead of after.&lt;/p&gt;
&lt;p&gt;The lifecycle is gated. A canvas advances only when its sections are non-empty, affected components are named, and lifecycle checks pass.&lt;/p&gt;
&lt;p&gt;&lt;svg id=&quot;mermaid-0&quot; width=&quot;100%&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; class=&quot;flowchart&quot; style=&quot;max-width: 255.6875px;&quot; viewBox=&quot;0 0 255.6875 478&quot; role=&quot;graphics-document document&quot; aria-roledescription=&quot;flowchart-v2&quot;&gt;&lt;style&gt;#mermaid-0{font-family:arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-0 .error-icon{fill:#552222;}#mermaid-0 .error-text{fill:#552222;stroke:#552222;}#mermaid-0 .edge-thickness-normal{stroke-width:1px;}#mermaid-0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-0 .marker{fill:#333333;stroke:#333333;}#mermaid-0 .marker.cross{stroke:#333333;}#mermaid-0 svg{font-family:arial,sans-serif;font-size:16px;}#mermaid-0 p{margin:0;}#mermaid-0 .label{font-family:arial,sans-serif;color:#333;}#mermaid-0 .cluster-label text{fill:#333;}#mermaid-0 .cluster-label span{color:#333;}#mermaid-0 .cluster-label span p{background-color:transparent;}#mermaid-0 .label text,#mermaid-0 span{fill:#333;color:#333;}#mermaid-0 .node rect,#mermaid-0 .node circle,#mermaid-0 .node ellipse,#mermaid-0 .node polygon,#mermaid-0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-0 .rough-node .label text,#mermaid-0 .node .label text,#mermaid-0 .image-shape .label,#mermaid-0 .icon-shape .label{text-anchor:middle;}#mermaid-0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-0 .rough-node .label,#mermaid-0 .node .label,#mermaid-0 .image-shape .label,#mermaid-0 .icon-shape .label{text-align:center;}#mermaid-0 .node.clickable{cursor:pointer;}#mermaid-0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-0 .arrowheadPath{fill:#333333;}#mermaid-0 .edgePath .path{stroke:#333333;stroke-width:1px;}#mermaid-0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-0 .cluster text{fill:#333;}#mermaid-0 .cluster span{color:#333;}#mermaid-0 .node .collapsed-indicator{fill:#aaaa33;stroke:none;opacity:0.6;}#mermaid-0 .node .collapsed-separator{stroke:#aaaa33;stroke-width:0.75px;}#mermaid-0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-0 rect.text{fill:none;stroke-width:0;}#mermaid-0 .icon-shape,#mermaid-0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-0 .icon-shape p,#mermaid-0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-0 .icon-shape .label rect,#mermaid-0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-0 .node .neo-node{stroke:#9370DB;}#mermaid-0 [data-look=&quot;neo&quot;].node rect,#mermaid-0 [data-look=&quot;neo&quot;].cluster rect,#mermaid-0 [data-look=&quot;neo&quot;].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].swimlane.cluster rect{filter:none;}#mermaid-0 [data-look=&quot;neo&quot;].node path{stroke:#9370DB;stroke-width:1px;}#mermaid-0 [data-look=&quot;neo&quot;].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].node .neo-line path{stroke:#9370DB;filter:none;}#mermaid-0 [data-look=&quot;neo&quot;].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].node circle .state-start{fill:#000000;}#mermaid-0 [data-look=&quot;neo&quot;].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 :root{--mermaid-font-family:arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-pointEnd&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-pointStart&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-pointEnd-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 11.5 14&quot; refX=&quot;11.5&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;10.5&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 11.5 7 L 0 14 z&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-pointStart-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 11.5 14&quot; refX=&quot;1&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11.5&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;polygon points=&quot;0,7 11.5,14 11.5,0&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/polygon&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-circleEnd&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;11&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-circleStart&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;-1&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-circleEnd-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refY=&quot;5&quot; refX=&quot;12.25&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;14&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-circleStart-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;-2&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;14&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-crossEnd&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 11 11&quot; refX=&quot;12&quot; refY=&quot;5.2&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 l 9,9 M 10,1 l -9,9&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-crossStart&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 11 11&quot; refX=&quot;-1&quot; refY=&quot;5.2&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 l 9,9 M 10,1 l -9,9&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-crossEnd-margin&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 15 15&quot; refX=&quot;17.7&quot; refY=&quot;7.5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 L 14,14 M 1,14 L 14,1&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2.5;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-crossStart-margin&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 15 15&quot; refX=&quot;-3.5&quot; refY=&quot;7.5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 L 14,14 M 1,14 L 14,1&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2.5; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;g class=&quot;root&quot;&gt;&lt;g class=&quot;clusters&quot;&gt;&lt;/g&gt;&lt;g class=&quot;edgePaths&quot;&gt;&lt;path d=&quot;M127.844,62L127.844,68.167C127.844,74.333,127.844,86.667,127.844,98.333C127.844,110,127.844,121,127.844,126.5L127.844,132&quot; id=&quot;mermaid-0-L_DRAFT_PROPOSED_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_DRAFT_PROPOSED_0&quot; data-points=&quot;W3sieCI6MTI3Ljg0Mzc1LCJ5Ijo2Mn0seyJ4IjoxMjcuODQzNzUsInkiOjk5fSx7IngiOjEyNy44NDM3NSwieSI6MTM2fV0=&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M127.844,190L127.844,196.167C127.844,202.333,127.844,214.667,127.844,226.333C127.844,238,127.844,249,127.844,254.5L127.844,260&quot; id=&quot;mermaid-0-L_PROPOSED_APPROVED_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_PROPOSED_APPROVED_0&quot; data-points=&quot;W3sieCI6MTI3Ljg0Mzc1LCJ5IjoxOTB9LHsieCI6MTI3Ljg0Mzc1LCJ5IjoyMjd9LHsieCI6MTI3Ljg0Mzc1LCJ5IjoyNjR9XQ==&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M127.844,318L127.844,324.167C127.844,330.333,127.844,342.667,127.844,354.333C127.844,366,127.844,377,127.844,382.5L127.844,388&quot; id=&quot;mermaid-0-L_APPROVED_CANONICAL_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_APPROVED_CANONICAL_0&quot; data-points=&quot;W3sieCI6MTI3Ljg0Mzc1LCJ5IjozMTh9LHsieCI6MTI3Ljg0Mzc1LCJ5IjozNTV9LHsieCI6MTI3Ljg0Mzc1LCJ5IjozOTJ9XQ==&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabels&quot;&gt;&lt;g class=&quot;edgeLabel&quot; transform=&quot;translate(127.84375, 99)&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_DRAFT_PROPOSED_0&quot; transform=&quot;translate(-28.9140625, -12)&quot;&gt;&lt;foreignObject width=&quot;57.828125&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;p&gt;propose&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabel&quot; transform=&quot;translate(127.84375, 227)&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_PROPOSED_APPROVED_0&quot; transform=&quot;translate(-66.703125, -12)&quot;&gt;&lt;foreignObject width=&quot;133.40625&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;p&gt;stakeholder review&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabel&quot; transform=&quot;translate(127.84375, 355)&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_APPROVED_CANONICAL_0&quot; transform=&quot;translate(-19.125, -12)&quot;&gt;&lt;foreignObject width=&quot;38.25&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;p&gt;apply&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;nodes&quot;&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-DRAFT-0&quot; data-look=&quot;classic&quot; transform=&quot;translate(127.84375, 35)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;stroke:#3b82f6 !important;stroke-width:2px !important&quot; x=&quot;-74.90625&quot; y=&quot;-27&quot; width=&quot;149.8125&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-44.90625, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;89.8125&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Draft canvas&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-PROPOSED-1&quot; data-look=&quot;classic&quot; transform=&quot;translate(127.84375, 163)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;stroke:#3b82f6 !important;stroke-width:2px !important&quot; x=&quot;-64.25&quot; y=&quot;-27&quot; width=&quot;128.5&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-34.25, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;68.5&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Proposed&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-APPROVED-2&quot; data-look=&quot;classic&quot; transform=&quot;translate(127.84375, 291)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;stroke:#10b981 !important;stroke-width:2px !important&quot; x=&quot;-64.25&quot; y=&quot;-27&quot; width=&quot;128.5&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-34.25, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;68.5&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Approved&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-CANONICAL-3&quot; data-look=&quot;classic&quot; transform=&quot;translate(127.84375, 431)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;stroke:#9b72cf !important;stroke-width:2px !important&quot; x=&quot;-119.84375&quot; y=&quot;-39&quot; width=&quot;239.6875&quot; height=&quot;78&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-89.84375, -24)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;179.6875&quot; height=&quot;48&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Canonical domain&lt;br&gt;&lt;/br&gt;(updated to agreed state)&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;defs&gt;&lt;filter id=&quot;mermaid-0-drop-shadow&quot; height=&quot;130%&quot; width=&quot;130%&quot;&gt;&lt;feDropShadow dx=&quot;4&quot; dy=&quot;4&quot; stdDeviation=&quot;0&quot; flood-opacity=&quot;0.06&quot; flood-color=&quot;#000000&quot;&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;defs&gt;&lt;filter id=&quot;mermaid-0-drop-shadow-small&quot; height=&quot;150%&quot; width=&quot;150%&quot;&gt;&lt;feDropShadow dx=&quot;2&quot; dy=&quot;2&quot; stdDeviation=&quot;0&quot; flood-opacity=&quot;0.06&quot; flood-color=&quot;#000000&quot;&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;/svg&gt;&lt;/p&gt;
&lt;p&gt;What the harness produces is &lt;strong&gt;a structured prompt per affected component&lt;/strong&gt;, not implementation code. Teams receive the prompt and use whatever technique they prefer to plan implementation - spec-driven workflows, their own templates, plain pairing. Teams retain autonomy over &lt;em&gt;how&lt;/em&gt;; the harness governs &lt;em&gt;what crosses the seam&lt;/em&gt;. This is the design choice that keeps team independence intact while ensuring cross-team contracts are verified before implementation diverges.&lt;/p&gt;
&lt;p&gt;The harness does not resolve domain model conflicts on its own. When two teams hold contradictory views on a shared concept, the harness surfaces the conflict, frames it as a structured question with options and consequences, and escalates. Resolution happens between humans. The harness produces the structured input that makes that conversation possible.&lt;/p&gt;
&lt;p&gt;The payoff splits cleanly across roles. For engineers, the value is &lt;strong&gt;pivot speed at contract level&lt;/strong&gt;. With the knowledge base loaded, the harness can answer &quot;if we add multi-context state, which contracts change and in which sequence?&quot; in a session rather than across a week of meetings. The cost of changing direction becomes visible while it is still cheap to change direction. For product managers, the value is &lt;strong&gt;informed scope decisions before the sprint hardens&lt;/strong&gt;. The decision harness gives PMs a structured view of how a feature idea maps to technical complexity and cost, before implementation starts. A PM sees not a green/yellow/red signal but the actual contract changes a feature would require - which is the information needed to choose between &quot;ship the small version now&quot; and &quot;fund the bigger change properly.&quot;&lt;/p&gt;
&lt;h2 id=&quot;context-engineering-at-the-architectural-level---the-stage-3--stage-4-jump&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#context-engineering-at-the-architectural-level---the-stage-3--stage-4-jump&quot; aria-label=&quot;context engineering at the architectural level   the stage 3  stage 4 jump permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Context engineering at the architectural level - the Stage 3 → Stage 4 jump&lt;/h2&gt;
&lt;p&gt;The maturity matrix describes the architectural governance dimension at Stage 3 as &quot;human-enforced boundaries; constrained solution space.&quot; Stage 4 is &quot;custom linters with remediation; taste invariants as code.&quot; The jump between them is what this article has been describing, decomposed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The knowledge base makes the architectural model machine-readable and agent-accessible&lt;/li&gt;
&lt;li&gt;The ingestion harness keeps that model current and challenged&lt;/li&gt;
&lt;li&gt;The decision harness encodes cross-team governance into a reviewable, versionable artifact&lt;/li&gt;
&lt;li&gt;C4 diagrams as code plus maturity validation gives the architectural view a freshness signal&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At Stage 3, &lt;strong&gt;context engineering&lt;/strong&gt; happens at the per-engineer or per-team level. Each engineer maintains their own context - &lt;code class=&quot;language-text&quot;&gt;AGENTS.md&lt;/code&gt;, structured prompts, local conventions. Each team curates its own slice of documentation. At Stage 4, context engineering happens at the platform level. The domain context that crosses team boundaries is a core artifact with the same lifecycle discipline as code: versioned, reviewed, validated, refreshed.&lt;/p&gt;
&lt;p&gt;The Harness Model thesis is that each stage unlocks the next. That is true. The corollary is what this article has been building toward: each stage also &lt;em&gt;constrains&lt;/em&gt; the next. If architectural governance stays at Stage 1 while everything else advances, the higher stages do not disappear. They accumulate debt. The weakest dimension does not impose a hard ceiling; it erodes the returns from everything above it. Individual velocity continues. The aggregate cost grows invisibly until contracts collide.&lt;/p&gt;
&lt;h3 id=&quot;15-minute-exercise&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#15-minute-exercise&quot; aria-label=&quot;15 minute exercise permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;15-minute exercise&lt;/h3&gt;
&lt;p&gt;Pick a team you work with - yours, or one you collaborate with. Sit with these three questions and answer them out loud, not in your head:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Do your teams share a documented domain vocabulary that both humans and LLMs can read - and is that vocabulary tested against working software, not just agreed in a meeting?&lt;/li&gt;
&lt;li&gt;Are your architectural invariants enforced by tooling (&lt;a href=&quot;https://handsonarchitects.com/blog/2026/protecting-architecture-with-automated-tests-in-python/&quot;&gt;linters, structural tests&lt;/a&gt;, maturity checks), or by code review? When a boundary is violated, how long before anyone notices?&lt;/li&gt;
&lt;li&gt;When a new team joins the platform, is there a structured way for them to ingest the existing contract surface - or does orientation happen through word of mouth and Slack threads?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These are diagnostic, not rhetorical. A team that cannot answer the first affirmatively does not yet have the substrate for a Stage 4 knowledge base. A team that cannot answer the second is running architectural governance as tribal knowledge - Stage 3 at best, regardless of how good its individual harness looks. A team that cannot answer the third is treating governance as a per-team property, which it is not: governance that lives in one team&apos;s heads is not platform governance.&lt;/p&gt;
&lt;p&gt;Spend the remaining minutes writing down one concrete artifact you could ship next week to move from a &quot;no&quot; to a &quot;partial yes&quot; on whichever question stung the most. A System Landscape diagram in version control. A first ADR. A glossary entry for the three terms your teams use differently. The first artifact is the one that costs the least and surfaces the next gap.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Task-level metrics will keep getting noisier. METR has been honest about why: the more AI adoption spreads, the harder it is to construct a control group, and the more the question itself drifts away from what teams actually want to know. Platforms are not built one task at a time. They are built by multiple teams converging on shared decisions about contracts they will all live with.&lt;/p&gt;
&lt;p&gt;Architectural governance is the dimension that crosses team boundaries. On an AI platform - where standards shift faster than annual planning cycles and where multiple teams ship in parallel - it is also the dimension most likely to lag while everything else advances. The Stage 3 → Stage 4 jump is not a tooling decision. It is the decision to treat context engineering as an architectural practice, not a per-team one: a shared domain model, an ingestion harness that keeps it current, a decision harness that turns cross-team disagreements into structured prompts before implementation diverges.&lt;/p&gt;
&lt;p&gt;The weakest dimension dominates the outcome. The question is whether the architectural governance dimension dominates by design - through a governed system that is owned, maintained, and machine-readable - or by neglect, through the cost of a migration nobody budgeted for. Both options ship code. Only one ships a platform.&lt;/p&gt;
&lt;div class=&quot;summary-box&quot;&gt;
&lt;h2 id=&quot;tldr&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tldr&quot; aria-label=&quot;tldr permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;On an AI platform, maturity doesn&apos;t average across dimensions - the weakest one dominates the outcome. Here are some signs that architecture governance is your weakness: AI standards shift faster than planning cycles, multiple teams ship in parallel, and on a platform without a dedicated architect role, no single team owns the cross-team seam.&lt;/p&gt;
&lt;p&gt;The cost stays invisible until contracts collide. Two teams use &quot;external integration server&quot; to mean different things; the collision surfaces at the security layer, and a documentation gap becomes a production incident. For an LLM the vocabulary &lt;em&gt;is&lt;/em&gt; the contract - when it is vague, the model guesses, and it guesses faster than the team can catch the guess.&lt;/p&gt;
&lt;p&gt;The way out is to stop treating context engineering as a per-team habit and make it an architectural practice. That starts with a machine-readable shared domain model - bounded contexts, C4 diagrams as code, and ADRs - kept current by a conversation-driven &lt;em&gt;ingestion harness&lt;/em&gt; that challenges the model until ambiguity is gone and materializes findings into a reviewable artifact rather than writing straight into the canonical store.&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;decision harness&lt;/em&gt; then turns cross-team contracts into reviewable artifacts before implementation diverges. It runs a feature through a REASINS canvas - Thoughtworks&apos; REASONS adapted to swap Operations for Integration - which forces ambiguous vocabulary to resolve to a named entity and catches the contract collision before any code is written. The harness governs &lt;em&gt;what&lt;/em&gt; crosses the seam; teams keep autonomy over &lt;em&gt;how&lt;/em&gt; they implement.&lt;/p&gt;
&lt;p&gt;That is the &lt;a href=&quot;https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/&quot;&gt;Stage 3 → Stage 4&lt;/a&gt; jump: context engineering as a platform artifact with the same lifecycle discipline as code. Both neglect and design ship code; only one ships a platform.&lt;/p&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[E2E test harness for AI agents: better specs, not smarter models]]></title><description><![CDATA[Three weeks of green CI. A flow that never executed the way the spec said it should. An agent that, given the choice between the documented…]]></description><link>https://handsonarchitects.com/blog/2026/harness-engineering-for-e2e-tests/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2026/harness-engineering-for-e2e-tests/</guid><pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/ac6adc2822b34c5be6204e24c942a246/c5394/harness-engineering.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAADBklEQVR42i2S2U8TURTG57/wwRffNGriEsOTmiCuQQmaYOCBgA9qE0JwDSGKccOIEBKMWKk8KBKXYFkE2ukgdMcC7XTWbtB2hjJMO22ZTtvp0M16NSY3v5x7knPOd04+KB7dRF0rXpogcZQgUL+X9NI4jjkJ3AWIosteLw5izL3iodxFJVVUxH9MFRQRinIsYtA7ftnMpkWr1Tg7O7O0ZIUNegMCm0yLIADkImFJFMQkvyOLSnYbsJCT8rkUlE7FGTYUCgbY8HqM2xD4SFoUEgInJngxGQVMClwmFf9dlAuKtBagPSRGEC4UX5bTCSjBbyxPjHlW7WyAWvE5YlEuIyXLpXy5tFPOZ8qFbKVSKZcLoFKRRY6LcGHmC/yh6XFDMspD23EOc1i9FEYRrqlPanrVyHOhF88ezs9py0U5l00Nv+436icqlTxQMfRevWS3OSlrr+YVGwpCoB+QJ8sS5Xa8aDmDjKuDHtvbOw32CQ2YabMsdDdWjT5XFQsFy6qp+sjukYEekP9dyhd30hDQA/YBX0J97/bxPd8HuhAnee2lRvP5G7K4MDL88VGrqv/J45APHxjvbWjcr5sc1XwYpSisUlZAcQqM9TkWTKqjnbVHG+rrOt58qe/qO1zXeuxM/dMHg9cvd/f1DC4gX690155uOVTTWLtr3149rAOLQJWi3Nb5sLPmoKn9/MzNc12nDuy9euts99ilu6NHrtw/V9t24+ZIe5vmwsWO06oa1UBrc1/TiebqqpNnPTQOgZMi88jXkSHMoCWRCaN27Puc4YfFqTXYv03OwXM6k9EB6y3Tk3p4cdawpNOZp6fhyampqUSMg0o7GZxwzht/kh46m8u4cTfLrEtSPECjLrcLdlExPhzbYmifiwqgwtYm6XYHAvQyapXTSaikpDFs1WYzMUF/jGMIirTYzV4/ucmsOQlCPWtmQ36fh3w//m7GrGWDazSFMSG/0/krIwp/rw16ZKUEcBWw6mYkxP1/YYHfSG4xIMlzTIQNprcFYEngzXxOKirpfC71Bx1vUrUU2FttAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;E2E test harness for AI agents&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/ac6adc2822b34c5be6204e24c942a246/3c492/harness-engineering.png&quot;
        srcset=&quot;/static/ac6adc2822b34c5be6204e24c942a246/ff46a/harness-engineering.png 325w,
/static/ac6adc2822b34c5be6204e24c942a246/a6d36/harness-engineering.png 650w,
/static/ac6adc2822b34c5be6204e24c942a246/3c492/harness-engineering.png 1300w,
/static/ac6adc2822b34c5be6204e24c942a246/c5394/harness-engineering.png 1535w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Three weeks of green CI. A flow that never executed the way the spec said it should. An agent that, given the choice between the documented OAuth dance and a debug shortcut that happened to return the same HTTP status, picked the shortcut. We fixed the documentation. The next test came out right on the first try.&lt;/p&gt;
&lt;p&gt;That bug is the small story. The bigger story is the harness engineering we did around it - over three and a half months, alongside normal feature work, on a Spring Boot microservice - the &lt;strong&gt;MCP Servers Registry&lt;/strong&gt; - that manages server configurations and OAuth flows. Fifteen end-to-end suites. 162 test steps. CI wall-clock flat at three to four minutes while coverage grew five times, and a &lt;strong&gt;fivefold drop in commits per test step&lt;/strong&gt; as the harness matured. A pipeline where four specialized AI agents propose, write, review, and run E2E tests, and where the way we move work between them is itself the artifact we are now optimizing. This post is the engineering tour of that harness - what we built, what we measured, and what we would warn the next team about.&lt;/p&gt;
&lt;h2 id=&quot;tests-were-green-the-agent-was-testing-the-wrong-thing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tests-were-green-the-agent-was-testing-the-wrong-thing&quot; aria-label=&quot;tests were green the agent was testing the wrong thing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tests were green. The agent was testing the wrong thing.&lt;/h2&gt;
&lt;p&gt;Open the CI dashboard. Green for three weeks. A new OAuth test had been merged, the suite added one more dot to the row, and nobody had a reason to look harder. The discovery did not come from staring at the test. It came from the Planner agent - one of four we run on this codebase - flagging a coverage gap while drafting a new scenario. The &quot;real&quot; OAuth flow it was looking at, the one with discovery and Dynamic Client Registration (DCR), was not exercised anywhere.&lt;/p&gt;
&lt;p&gt;Tracing the gap led back to the existing test. It had been calling an internal debug endpoint instead of the discovery and registration calls the contract described. The HTTP statuses lined up. The assertions passed. The test was wrong about which flow it was testing - and &quot;wrong&quot; had been silent for three weeks of merges.&lt;/p&gt;
&lt;p&gt;That endpoint existed for a good reason. Debugging OAuth against external MCP servers is hard, and a shortcut that hands you a working token end-to-end saves hours. The endpoint is internal to the service, fully admin-gated, and not exposed in production - it exists so engineers can debug OAuth integrations without losing an afternoon. The endpoint was never the problem. The problem was that the API contract did not say &quot;for local debugging only; not part of the OAuth flow.&quot; The agent saw an endpoint that produced the right status at the right step, and used it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We didn&apos;t fix the test. We fixed the documentation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We will come back to the mechanics of that fix later in the post. We start here because this incident is the cleanest statement of the principle the entire harness is built around: &lt;strong&gt;test quality is a function of what the agent receives as input.&lt;/strong&gt; The spec decides. The agent executes. We do iterate the agents and skills too - more accurate planners, more specialized review skills - but that work moves slowly compared to the context the agents read, which changes every sprint. It is a small simplification, but mostly true in our case: the leverage is in the spec, not the model. Once you accept that, the question shifts from &quot;how do we get better agents?&quot; to &quot;what is the system that produces the spec the agent reads, and how do we evolve it without slowing delivery down?&quot; The rest of this post is the answer we have so far.&lt;/p&gt;
&lt;h2 id=&quot;why-e2e-became-the-place-the-harness-took-shape&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-e2e-became-the-place-the-harness-took-shape&quot; aria-label=&quot;why e2e became the place the harness took shape permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why E2E became the place the harness took shape&lt;/h2&gt;
&lt;p&gt;One of us joined the service while it was under delivery pressure. Documentation was a handful of API examples in Postman JSON format. The unit and integration tests had high coverage and low business value - vibe-coded by AI, they checked HTTP 200s and response shapes, not behaviour. CI was green on every push and told us almost nothing.&lt;/p&gt;
&lt;p&gt;We had a choice about where to start. We picked E2E deliberately. Three reasons. First, an E2E suite asserts on business outcomes - &quot;the admin published a server and it now shows up in the list&quot; - which is exactly the layer of behaviour the existing tests were not protecting. Second, an E2E test exercises the integration boundary, which is where a brownfield Spring Boot service hides most of its scars: WireMock stubs for external dependencies, OAuth flows, secrets handling, real database transitions. Third - and this is the part that surprised us - an E2E suite makes the missing documentation visible. You cannot write a useful E2E test without naming the precondition, the endpoint, the expected status, and the external stub. Either the artifacts that name those things exist, or they do not. The suite forces the question.&lt;/p&gt;
&lt;p&gt;The work happened over roughly three and a half months, in sprint-sized increments. Ten weeks on the foundation - C4 system and container diagrams (kept in Mermaid alongside the code, generated with AI from a separate architecture spec and refreshed when the spec changes), a real API contract, a &lt;code class=&quot;language-text&quot;&gt;CONTRIBUTING.md&lt;/code&gt;, a &lt;code class=&quot;language-text&quot;&gt;README.md&lt;/code&gt;, and the first scenarios in what would become &lt;code class=&quot;language-text&quot;&gt;SCENARIOS.md&lt;/code&gt;. CI that verified instead of only building. Two weeks of harness work on top - &lt;code class=&quot;language-text&quot;&gt;CLAUDE.md&lt;/code&gt; as the index the agent reads first, plus the four-agent pipeline (Planner, Implementer, Reviewer, Runner) we still run today. Then the suites started landing. March: three suites and 33 steps. April: nine new suites and 83 new steps. May 1–11: three more suites and 46 steps - eleven days, more than half of April&apos;s step count. We will get to the data in detail. As a rough order of magnitude, the foundation phase took about a day per sprint per engineer; the expansion phase, a few hours.&lt;/p&gt;
&lt;p&gt;Mapped onto &lt;a href=&quot;https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/&quot;&gt;the Harness Model maturity matrix&lt;/a&gt;, the journey is the climb from Stage 1 (no AI process, tribal knowledge, ad-hoc tests) into Stage 3 (human-in-the-loop with structured documentation and an agent that generates from it). We did not jump. We climbed. If you are earlier on the curve and still asking where to start with AI-assisted development at all, our companion post on &lt;a href=&quot;https://handsonarchitects.com/blog/2026/how-to-start-with-ai-assisted-development-today/&quot;&gt;how to start with AI-assisted development today&lt;/a&gt; covers the thirty-minute experiment we recommend before any harness work. The same documentation that onboarded a new engineer turned out to be exactly what the agent needed, and that coincidence is the part we keep coming back to. There is no &quot;AI documentation&quot; in this repository. There is just documentation, written well enough that an agent can use it.&lt;/p&gt;
&lt;h2 id=&quot;domain-aggregates-where-the-spec-actually-starts&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#domain-aggregates-where-the-spec-actually-starts&quot; aria-label=&quot;domain aggregates where the spec actually starts permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Domain aggregates: where the spec actually starts&lt;/h2&gt;
&lt;p&gt;Before any SCN, before any contract, there is a smaller artifact we under-invested in for the first few weeks and have been correcting for ever since: the &lt;strong&gt;domain aggregate&lt;/strong&gt; - DDD&apos;s aggregate, sketched on one page. A single business concept with its states, its lifecycle transitions, who owns each transition, and the invariants that survive across them.&lt;/p&gt;
&lt;p&gt;For the MCP Servers Registry, the aggregate looks roughly like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Domain aggregate: server configuration (MCP Servers Registry)
States:        UNPUBLISHED -&gt; PUBLISHED
Owners:        admin edits (create, update, publish, unpublish)
               power user reads (selects for runtime, never edits)
Transitions:   create               -&gt; UNPUBLISHED
               update (admin)       -&gt; UNPUBLISHED
               publish (admin)      -&gt; PUBLISHED
               unpublish (admin)    -&gt; UNPUBLISHED
Invariants:    a power user only sees PUBLISHED entries
               server URL and configuration ID are distinct fields&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This kind of artifact belongs &lt;strong&gt;before&lt;/strong&gt; SCNs in the spec lineage, not alongside them. Two reasons. First, every SCN that touches &quot;publish&quot; or &quot;select&quot; needs the same vocabulary; without the aggregate, three SCN authors invent three slightly different vocabularies, and the agent learns the inconsistency. Second - and this is the part the OAuth bug pointed at - most agent misbehaviour we have traced back to spec problems came from missing aggregate-level knowledge, not missing SCN steps. A field called &quot;server URL&quot; being silently overloaded to also carry a configuration ID is the kind of confusion that the aggregate catches and an SCN does not.&lt;/p&gt;
&lt;p&gt;We use AI in this step too, but as an elicitor rather than a writer. A skill in the repository prompts the engineer through &quot;what are the states, who owns each transition, what are the invariants,&quot; and produces a draft aggregate the team edits. The work that takes time is naming the states and the owners - exactly the work the SCN cannot do for itself. The aggregate is short, lives next to the API contract, and is referenced by every SCN that crosses its boundary. The next section is where it gets cashed in.&lt;/p&gt;
&lt;h2 id=&quot;the-scn-format---and-where-it-sits-in-the-spec-lineage&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-scn-format---and-where-it-sits-in-the-spec-lineage&quot; aria-label=&quot;the scn format   and where it sits in the spec lineage permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The SCN format - and where it sits in the spec lineage&lt;/h2&gt;
&lt;p&gt;Every E2E test starts as a &lt;strong&gt;scenario&lt;/strong&gt; in &lt;code class=&quot;language-text&quot;&gt;SCENARIOS.md&lt;/code&gt;. We call them SCNs. The format is intentionally small.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;SCN-001: Admin configures MCP server

Preconditions:
- Application is healthy (actuator/health -&gt; UP)
- Admin authenticated (identity headers + userType=ADMIN)
- WireMock as stub for the external MCP server

Steps:
1. Admin reads the current server list.
   GET  /mcp-servers/config -&gt; 200
2. Admin tests the connection to a new server; tool discovery succeeds.
   POST /mcp-servers/test   -&gt; 200, discover tools
3. Admin saves the configuration as UNPUBLISHED.
   POST /mcp-servers/config (state=UNPUBLISHED) -&gt; 200
4. Admin publishes the configuration.
   POST /mcp-servers/config (state=PUBLISHED)   -&gt; 200
5. Admin re-reads the list; the new entry is present.
   GET  /mcp-servers/config -&gt; 200, count + 1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Five steps. Three preconditions. Each step has an HTTP verb, an endpoint, and an expected outcome. That is the entire interface between human intent and the agent.&lt;/p&gt;
&lt;p&gt;The format is not new. &lt;em&gt;Specification by Example&lt;/em&gt; and Gojko Adzic&apos;s work on it predate this by more than a decade. BDD tools like Gherkin gave us Given/When/Then. Contract testing tools like &lt;a href=&quot;https://docs.pact.io/&quot;&gt;Pact&lt;/a&gt; push the same idea down to the wire. More recently, Martin Fowler&apos;s writing on &lt;a href=&quot;https://martinfowler.com/articles/structured-prompt-driven/&quot;&gt;structured prompt-driven development&lt;/a&gt; and projects like &lt;a href=&quot;https://github.com/github/spec-kit&quot;&gt;GitHub Spec Kit&lt;/a&gt; treat structured prompts as a first-class engineering artifact. SCNs sit inside that lineage. The shared idea is older than any of the tooling: there is a layer of artifact above the test, written in human language, that captures intent precisely enough to survive change.&lt;/p&gt;
&lt;p&gt;The difference here is &lt;strong&gt;who the consumer is&lt;/strong&gt;. Gherkin &lt;em&gt;is&lt;/em&gt; the test, executed by Cucumber. SCN &lt;em&gt;generates&lt;/em&gt; the test, written in Python by an agent. That distinction is why we keep the SCN minimal. The agent fills in fixtures, WireMock stubs, assertions, and cleanup; we keep the document at the level a product manager could read. If we pushed the SCN to be more precise, we would be writing the test in slow motion. If we let it drift looser, the agent would invent details.&lt;/p&gt;
&lt;p&gt;The other job the SCN does - and we keep underlining this - is to act as a &lt;strong&gt;contract&lt;/strong&gt;. It is the precise statement of what &quot;this flow works&quot; means, written before the test exists, and it stays the contract whether a human drafts it from a feature ticket or the Planner drafts it from a contract delta. The SCN is also a &lt;strong&gt;diagnostic&lt;/strong&gt;: somebody has to sit down and write the steps, preconditions, and success criteria. If neither the human nor the planner can, the flow is not understood yet - and no model will fix that for you.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you can&apos;t write the scenario, the agent can&apos;t either.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We treat that sentence as a hard rule. To make the diagnostic concrete, here is an SCN that does not work - and the small set of edits that does:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;SCN-XXX (weak): User publishes server
Steps:
1. Save and publish the server configuration -&gt; success
2. Check that the server is listed&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The verbs are missing. &quot;Success&quot; is unspecified. There is no precondition about authentication or what the list contained beforehand. An agent reading this writes a test that calls &lt;em&gt;some&lt;/em&gt; endpoint with &lt;em&gt;some&lt;/em&gt; payload and asserts a 2xx - and the test is green for the wrong reasons.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;SCN-XXX (fixed): Admin publishes a new MCP server
Preconditions:
- Admin authenticated (userType=ADMIN)
- Configuration list is empty
Steps:
1. Admin saves a new server configuration as UNPUBLISHED.
   POST /mcp-servers/config (state=UNPUBLISHED) -&gt; 200
2. Admin publishes the saved configuration.
   POST /mcp-servers/config (state=PUBLISHED)   -&gt; 200
3. Admin re-reads the list; the configuration is PUBLISHED.
   GET  /mcp-servers/config                     -&gt; 200, count == 1, entry is PUBLISHED&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Same intent. Wholly different prompt. The fix is not in the agent or the model - it is in five lines of plain text that a human writes, or the Planner drafts and a human approves, before regenerating.&lt;/p&gt;
&lt;h2 id=&quot;the-oauth-bug-in-mechanics&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-oauth-bug-in-mechanics&quot; aria-label=&quot;the oauth bug in mechanics permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The OAuth bug, in mechanics&lt;/h2&gt;
&lt;p&gt;Back to the OAuth bug, this time with the wiring. We want to walk through it because it is the clearest example we have of &quot;fix the spec, not the test&quot; paying off in a single afternoon.&lt;/p&gt;
&lt;p&gt;The correct OAuth Dynamic Client Registration (DCR) flow looks like this:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Correct OAuth DCR flow&lt;/th&gt;
&lt;th&gt;What the agent did&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;GET /.well-known/oauth-authorization-server&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;skipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;POST /register&lt;/code&gt; (Dynamic Client Registration)&lt;/td&gt;
&lt;td&gt;skipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;GET /authorize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;GET /authorize&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;POST /token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;POST /token&lt;/code&gt; (against the debug endpoint)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;GET /resource&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;GET /resource&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The agent skipped discovery and registration and used the debug endpoint to obtain a working token directly. The remaining calls all succeeded. The test asserted that a resource came back. CI was green.&lt;/p&gt;
&lt;p&gt;The fix was two lines of work, neither in the test file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;diff&quot;&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;## OAuth endpoints
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;### POST /oauth/debug-token
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;For local debugging of OAuth integrations only. NOT part of any production
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;OAuth flow. Tests MUST go through /.well-known and /register first.
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;### GET /.well-known/oauth-authorization-server
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;Returns the authorization server metadata used by the DCR flow.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We added the missing line to the API contract describing what the debug endpoint is for. Then we wrote a detailed OAuth request-flow diagram into the architecture docs - a Mermaid sequence diagram listing the correct endpoints in order, AI-generated from our OAuth spec following the same pattern as the C4 diagrams.&lt;/p&gt;
&lt;p&gt;On the next Planner iteration, the Planner discovered the new file as part of its context, noticed that the existing test did not match the documented flow, and proposed a regenerated test. We accepted it. The new test made the discovery call, performed the registration, used the registered client to get a token, fetched the resource, and asserted the lot. One shot. Fix the spec, not the test: write the missing artifact, let the harness re-read it, accept the corrected test.&lt;/p&gt;
&lt;p&gt;The agent was not confused about OAuth. It knew the protocol perfectly well. What it was missing was the boundary statement - which endpoints in &lt;em&gt;our&lt;/em&gt; service are part of the flow and which are not. That is exactly the gap an aggregate-plus-contract pair is supposed to close, and the lesson we took out of the incident was that the domain aggregate for &quot;MCP server OAuth integration&quot; had been implicit in our heads and absent from the repository.&lt;/p&gt;
&lt;p&gt;The implication compounds both ways. Every future OAuth scenario the agent generates now starts from the corrected understanding - it cannot accidentally use the debug endpoint. Every other agent, and every human reading the docs for onboarding, gets the same clarification for free. A line of API contract is doing more work than a fix in a single test could.&lt;/p&gt;
&lt;h2 id=&quot;the-artifact-loop-plus-whats-upstream-and-downstream-of-it&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-artifact-loop-plus-whats-upstream-and-downstream-of-it&quot; aria-label=&quot;the artifact loop plus whats upstream and downstream of it permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The artifact loop, plus what&apos;s upstream and downstream of it&lt;/h2&gt;
&lt;p&gt;The artifacts are not isolated documents. They form a loop. The loop is what makes the investment compound.&lt;/p&gt;
&lt;div class=&quot;diagram-desktop diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/a20d625cc5b9d218a5db488771fb312d/14747/artifact-loop.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17.846153846153847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA2UlEQVR42iWO3WrCQBCF8/4v0XsLBaW00II3FdGWpkiVqiTdNqkhZptsEpPd7M/MuOLhYzhwZpgTFK0rWlCWSP2qfMq3k+E4RZkgEiAQEhvKj+Hvhe9ClW6GHADwIvIKnt8Pr3FfSSK5r+PRIbxp2S3IyMeXFaQvlT9Vmzu2eDyu3mSiteWx3C+L9SwLHhbf862o/bEVrmcgE/BTi86aRktldGV7pspUix9VZrpxDtJVs7xn83EchFHzmZq69w3RIV0xDrKTYDXPWuG7+/9XvLfWdZU5cd39mzMAod4DNftfggAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Artifact loop diagram showing Spec, Generate, Run, and Sharpen stages connected in a cycle, with the sharpened spec feeding back as input for the next test generation&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/a20d625cc5b9d218a5db488771fb312d/3c492/artifact-loop.png&quot;
        srcset=&quot;/static/a20d625cc5b9d218a5db488771fb312d/ff46a/artifact-loop.png 325w,
/static/a20d625cc5b9d218a5db488771fb312d/a6d36/artifact-loop.png 650w,
/static/a20d625cc5b9d218a5db488771fb312d/3c492/artifact-loop.png 1300w,
/static/a20d625cc5b9d218a5db488771fb312d/14747/artifact-loop.png 1914w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-desktop diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/9551e18e97db521bf33f846116c1db26/14747/artifact-loop-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 17.846153846153847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAABLUlEQVR42mNQsfRLUrbwT1FP2sDrbCgYbmcoczfc0+CMjYHMQ3tD8QgGILC3t+cA0bJWOkViukr3NR1Nz0oaqt6SMFFbAhL/X/+fiYGhHoJtfBP+a1oH/FdyyTZxMxFJ87DV+B/pZ/bDzVrtv5OheA5Ig4eKBzuIlrPW6VOzNfxv7uv0U8fZ/L+MuebxW7f+s+srO6y11ve55WIRepXBJSTtv4Fj6H8FyzC7NGsOORtDyVxrA/Fka33xHG97JR3DIC95czdHY/tADxkZZxNTRVujbDlrvWQVO6MsBUdDv5kz/7OaqruddbeK+O9tF/OfwdAp/LyKRcB5DZdoCwYwADn/PyOIlZs7kd3UyyXM1Mst19bX158BDfz//59x4sRt7DZ6gR4mmq4xxuouUQAi8FlZY4KSLwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Artifact loop diagram showing Spec, Generate, Run, and Sharpen stages connected in a cycle, with the sharpened spec feeding back as input for the next test generation&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/9551e18e97db521bf33f846116c1db26/3c492/artifact-loop-dark.png&quot;
        srcset=&quot;/static/9551e18e97db521bf33f846116c1db26/ff46a/artifact-loop-dark.png 325w,
/static/9551e18e97db521bf33f846116c1db26/a6d36/artifact-loop-dark.png 650w,
/static/9551e18e97db521bf33f846116c1db26/3c492/artifact-loop-dark.png 1300w,
/static/9551e18e97db521bf33f846116c1db26/14747/artifact-loop-dark.png 1914w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-mobile diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 574px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/4e45959c53db5c3c9662c41698e45abc/86389/artifact-loop-mobile.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 209.53846153846155%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAqCAIAAAA8m+yHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAE3UlEQVR42o2UZ1PjSBCG+f8fr+7b3VVtqL1a7mC5hSWDAScwlmw5W7KRZOXkoBxndG2zAUzYVb3VnpnS0297pjUb+YMHr+KdjvsyptWlxmYOuh8zGu5JKEp+vL/xFL7lUEPEHRm1xKw8cEr9BSWkLQn3VFxk0CLA3998BN8/bRExOuJn4JnuluiPJ62O4Il2zlm4MUFO+CrcEhCtY4r3im3tsMaDSl2j0FQhXZ1DTvAzmDFy8s45qvGFpnxGSlctrdjRlzD/M5gS0MjMwe2MFK+H1ikhQhaAWSsn+F9wpvW8PlqUutrB9d3hDXfekApNhbXwL8K4K4Zgft6QD2/4o9qkxix+CYbdhrIZPTmucZeUVO5o+9VxV/QlOyde2e37JZJHcCRwyB057yhw4HDCOSUikosr458dlR3kEyPU7cx0sOEg85taI92wM4TXOww/UJ5laRSFj9cRTOIkieP4oc3jsldZF45vOz4MEMKw8l1g6vrhYxilyKohq4ysCppd5x7haUVfvcp9Ak2ry0XzCk3JVWrkrcE48ZF5ir1q7lYi/Ww23pky2za/a9FbsXme+9fYvkTG+QtwGiDrArs3edxYCEd04ff+2W93lT/Yyp+eepbHTWQXwfwe9oOnMDhEhN7dnNTfTNk9g9nRepsWsyOQ7xb8Qe6VM/PyVTi8dcVDkXhr0dsQzcGW1t1UqA+RUcjdYmZevAT7aHqRh0Tm1uT25nS8p/W3rfGuPvgEg9SpYb+avVR2noZI/oz0XVA+O8DTfbyK+fQAhIwvmbqVKftLGD2FIWPi4GSxVDzPEzv2zNA1YIDj5WIOyvyVM17f7aftGSdpGMXfGzjA2TyLFmkwTwPVm0NcZMEs9iKUbuC15nwAo1XHkQr3oVnZpG4Oufa/7dr729Knbv09Ue6r0jPOSQK9/aOHE5QFaeym4SIJDM+208BOQjsNY3CGHzmytdhRY0eJbCPzeprU1yUj82EKi3rsgnyUQGHrG3bjTI4duhJwJZ+9VzngKiH/Y+qzBW/UdBV421uDSU+qxZNeprYTuYe0RiD8RR6/bxd6mdZJFVA3VZqJ1PHU5+GbiAeSiiUimJyo7S8T4kBslGd0MxJBrUQmY7H9Enwd8uDcxzr47LC1A7F5qra3x9ddpPWQ3k4UMhaehxueVE+EypT+u3v5mb39OCgdy9THfnF7dL05LEH91fmolcrfyg4ewXVXrEYcFHw5HRRndHFOV53x1WxYWjAQL2dDIpxAdmq1Ya7/GGZ8s2pzTV+mAqUZKFSoEK4Iaq6mICgNTmQSzp+Bn2nPKAnD+Mm9mKdp9uQmWV17D9szg68nDJ8mhZ4F/jXn0IEvIJMnM/7OhC/AsSJ3GoNgYCquN0u8eYK/3d0ba/eu1HMndVto2Fx9rrR9teMrbU9ueULDAUmUy9YXcZjd/6l1WKU9peObwwB0/k93/11j7y25+4Zk69PpKNYHgUi5L8LKcOmj9f1hRb894hqnk9oB9A3bKkhSyzEGgdB0XoUp1xgGN/t31b1R90qBCCrvMkLTNochFP8K7Oq9oH0hFbZ6zTOh+B9NnvDECV/6zNSPOWNZ9suw2HGEujMhbKZijaoWUzEhggZFHaJMeWxtEQcvwFMpVGhPvwuMe7FfZbKhwYba2FfHfpZ+Par/ATYvJ43k0ZYhAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Vertical artifact loop diagram showing Spec, Generate, Run, and Sharpen stages connected in a cycle, with the sharpened spec feeding back as input for the next test generation&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/4e45959c53db5c3c9662c41698e45abc/86389/artifact-loop-mobile.png&quot;
        srcset=&quot;/static/4e45959c53db5c3c9662c41698e45abc/ff46a/artifact-loop-mobile.png 325w,
/static/4e45959c53db5c3c9662c41698e45abc/86389/artifact-loop-mobile.png 574w&quot;
        sizes=&quot;(max-width: 574px) 100vw, 574px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-mobile diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 574px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/435e58d48904b3f32d9adaf09054a48a/86389/artifact-loop-mobile-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 209.53846153846155%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAqCAYAAACz+XvQAAAACXBIWXMAAAsTAAALEwEAmpwYAAAHhUlEQVR42pWXW1Ab1xnHZTvxUNduwkU3hKTVXVpJq8tKK2ml1V2LhJCEABnEveBgYurEHZqx07gV8dSAUdzExeOGAUKTlKSjmbZMJy95YtLxY2bahz71oZ7mscN08tAYm130dVdASws4+Mx8Okfn7Pnt//v2O3vOCgSHSuk0/4vl3+w1xMbmjNGxn3L1tJLqn1b6+6cNkbFpY/xSyRAduYW2X7tBvrF+YW/iKcGRpVA4w1fGxPi6mmgHxNHKqPAU2KI9YI8VQeNuA8SZrCodCdBRxa/JyU+ad3XsCjmKWANqqeJv5ZYwSAzerRbUzyR7X2U6R6cYHU4zQo17W2Ykq4grs4mPL0oPenY8MNi/LrdGwOhJMZ7WIkS7XoFo5yvgTvSAL9VflRq8oPbkviZHl5tPBNRxwBZLCMxkhonkL4E/PQhUZhi8yT4O2lttNvqeExjqX5eZg+Cme5lA+xA4Il0QzI5AJD8GBN1blZlIQIjs8ymUW8NgDXQwvJsxzmXeAukhINsGqjLU/3xAbWjgd3wMtS76KQfc8aeHdrg47nAKd2zBPMsDVUTun9T4x9ITKiyuy7kYKiwUE+Xc9KcHgOCU0oXLoHPRVbHWBWoicwKFe3loSIx/hOBpBnGl/yG3t27WzJHclGGJTQRv29QS7Y+1wb6/ha6sSnYnwjGJvZfx+LWPpR0/WvZFr66gwdc+0AUmP9TvGzmxbLh861fdgclP9YJC5cwRogRnQiHBC/vGdZ0plUp1c3NzLx1318XFP5wrlz/6ruAEpaZwbePLppXKH4U1Z+CgO1ybW2bji4svlhbXmo4kjCdl1zoC4vc7Kcm9rK9pIWE//26O0nzSGdL8Oo6d/3mWFC10UZL3OgOSpR/k5CO1OePjL84sfNh4CMbfnXaLH5rVQrBomljC2gJ0wARJygQRjxZSITO4LTIwqRoZu14Eeb/0N7thKpy9u1RpOBIYc4q+MKmaQN184QmFq5nLA3FmYjDGDuX97FCXn/FgcoYfw3RCyPgka/vAB2tr9UcC4y7xQ638ZcjEMLaY9ULMZwCaQiEbt0EyiMLFdjeECS1jVjdAhpR8+l+FSw3HAg1IPVA4wvZwk1McpKedgLawBXIctCvpBJdZxlh1jZDznwAYdYge6hSNYFI3sTnaCTG/CdoiGMRJE6TCVkhHbdyYkEHVTdDuPQGwOyj9gsLEELQJn3jResZnbmS8aMN/at4CmHAr6pRAf0xaA4ZCobqZhYXGvTQ7vWe76XX3h2jDj/sV0tKwUnL/CiopD4hF717PIfNvdWjKE2LRPNfHj392Q1EPf0HP7r7xS6cfrH1WLzhpuVupfOf+/cr5g8neYDabxDZLWmHTpeWYIYNFA70KnP9vTGtwS4fSY0P2L/5/E5QWF8+VSve+t+cevxwFwY7sEt3HvRt7CixORyDSnYfUQD+EOnNscrAPMsPDV59LoRpFFRqnFdd5bYSOcFJuOpY0+Fyk3ov7EbcjaMRxqcA4M9houFkwmN/q0aA3Clr7zaJOMtWm7L/zk1hPuRSWvhZX8H2mNzp1mndG5IaF0Wbp4vg54Nb0kTFEMuSszIuC2K7bkjh0zL6JHXpWZNOyB/skDu1TqcsIqmLoEj93qlQWHXrKchqfl2IaEOrk22KjEpq0MpBZNNA23MXFqr0q1MtBZFDsml7OilEEVAVqjAe+efue8JBCOe2el9q03ATlttiEQLNVA9YIAU46APY4CRrCAmJUBRLOREYlKzarvg2I14BNnMJ6TTMIOSXe9ih37CDBEnKDNxOFBk41b9wYKzGrnw1UtBKzIu4ivce2Fe1pZ4h0hAl2Jxks5mHChTaGzMWZcHeKSfTlGLXb8kRoUHLA4OixQGXaMy916KAF027rSTvovFjNEByt1bzLetIGep8NZFbuIVk4hcXo7kMpPxAdAmpHkzTSQb4jS7mnxXHHrZolHG8r2jwzLSnvnJRr7/fL0563lVlyTjOZd/Jzb99bEZ546W1sPKr7/PM/H7cJnSqtrtbtbQGn/vdsWOLyh9/pDlpt8W+8cOPB7LGL/87y+oVKpXL2RDveVN995UDkpu3aaDk99eovOkcTJctk/o5zIjvruJT7mX28645rcrgcHmktOSfyZUvlqL354EugzTP2S6c2AXZN+F9OffypWUl9gyoCj80I9diCBB9jmsiWTRP5xoJQbAjr/uvi9S9fOvL0sA8M2S+u6GUEIGKMUYlt1Xzi+1DMXIGB3CQMdlwFTBMAeZNlhxsDpy7+1errGy8/ExhxFJeNcm6VSPFtAk1U497uatjdUU34ClWavFilnJmqSendUUkc4Dak/r76+p+eDQzbe1ZMCh+oxHYmGeiFdLAPSCwFfDtFFSEd6gczQu4gnEK3IXkShb1LWqkLAo7Mk056lA25smw2OsxGPZ1slMizmcggG/d08eEAl55+VPm2GKY8YyuYiju9qijGbaLBZUwAgbYC33abEuCzprk+umps8YHfkv/q96VHRyss7X1rDMSud9HE4HsJvO921NE7G3X0ze7Wuxa2XeTq4kwc75vPByevr5Ye1e2n3b8BOGYqhbuEFoAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Vertical artifact loop diagram showing Spec, Generate, Run, and Sharpen stages connected in a cycle, with the sharpened spec feeding back as input for the next test generation&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/435e58d48904b3f32d9adaf09054a48a/86389/artifact-loop-mobile-dark.png&quot;
        srcset=&quot;/static/435e58d48904b3f32d9adaf09054a48a/ff46a/artifact-loop-mobile-dark.png 325w,
/static/435e58d48904b3f32d9adaf09054a48a/86389/artifact-loop-mobile-dark.png 574w&quot;
        sizes=&quot;(max-width: 574px) 100vw, 574px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;That diagram is the inner loop - the one we sketch first when someone asks how the harness works. SPEC, GENERATE, RUN, SHARPEN. We have linked it before to Mitchell Hashimoto&apos;s &lt;a href=&quot;https://mitchellh.com/writing/my-ai-adoption-journey#step-5-engineer-the-harness&quot;&gt;harness engineering&lt;/a&gt; idea: any time an agent makes a mistake, you engineer a solution so it never makes that mistake again. The artifact loop is one shape that engineering takes. Custom linters and structural tests - like those in our &lt;a href=&quot;https://handsonarchitects.com/blog/2026/protecting-architecture-with-automated-tests-in-python/&quot;&gt;Python architecture-tests post&lt;/a&gt; - are another. Both belong in the harness.&lt;/p&gt;
&lt;p&gt;Concretely, skills live at &lt;code class=&quot;language-text&quot;&gt;.claude/skills/&amp;lt;name&gt;/SKILL.md&lt;/code&gt;, agents at &lt;code class=&quot;language-text&quot;&gt;.claude/agents/&amp;lt;name&gt;.md&lt;/code&gt;, and slash commands at &lt;code class=&quot;language-text&quot;&gt;.claude/commands/&amp;lt;name&gt;.md&lt;/code&gt;. The orchestration sits in a slash command - &lt;code class=&quot;language-text&quot;&gt;/e2e/scenario&lt;/code&gt; - that walks the four agents through the pipeline with a human approval gate after the Planner step. Each agent in turn calls a focused skill: the Implementer uses &lt;code class=&quot;language-text&quot;&gt;e2e-scaffold&lt;/code&gt; for the suite skeleton (templates for the test file, docker-compose, and WireMock layout) and &lt;code class=&quot;language-text&quot;&gt;wiremock&lt;/code&gt; for stub generation; the Runner uses &lt;code class=&quot;language-text&quot;&gt;e2e-run&lt;/code&gt; to execute the suite in an isolated container. Conventions live in the skills; control flow lives in the command; the agents are the things that get reviewed.&lt;/p&gt;
&lt;p&gt;What the artifact loop hides is the SDLC pipeline that surrounds it. Once we started building specialized agents for domain elicitation, contract drafting, and SCN authoring - not only for test generation - a wider loop became visible. The diagram below is how we draw that loop today.&lt;/p&gt;
&lt;div class=&quot;diagram-wide diagram-desktop diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/f32eae337b543ef7760b98996c1207c5/4abbf/sdlc-harness-loop.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 13.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAq0lEQVR42g3L2Q7BQBQA0P7/1whBeBDLiyWCNhUSYqm29qlp78wdc+8M5/0EyjipfXjS4624CO9gL09NSDvOXL33QGZRJqPbZg35m1Tvvm7nq6etHDqsKIjPWig/230G4e388gy74lCTxwZj9s8V4VBs68l8Wh7frLp52MqWDwtpJCetQxAnGr+u1CQVAzq2ivDFRqCBK8jKIDEbspbJEBVWF1Yh2Wwjo376A7D3pWxQKiiIAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Horizontal SDLC harness loop showing six numbered stations (domain aggregates, SCNs, API contract and C4 diagrams, SCN-driven test generation, CI verification, and a sharpen step) connected in a cycle and grouped by the five states they represent in the scenario lifecycle; each station names the specialized skill or agent responsible for it&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/f32eae337b543ef7760b98996c1207c5/3c492/sdlc-harness-loop.png&quot;
        srcset=&quot;/static/f32eae337b543ef7760b98996c1207c5/ff46a/sdlc-harness-loop.png 325w,
/static/f32eae337b543ef7760b98996c1207c5/a6d36/sdlc-harness-loop.png 650w,
/static/f32eae337b543ef7760b98996c1207c5/3c492/sdlc-harness-loop.png 1300w,
/static/f32eae337b543ef7760b98996c1207c5/d544a/sdlc-harness-loop.png 1950w,
/static/f32eae337b543ef7760b98996c1207c5/d2433/sdlc-harness-loop.png 2600w,
/static/f32eae337b543ef7760b98996c1207c5/4abbf/sdlc-harness-loop.png 3088w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-wide diagram-desktop diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/9824f63570ce6833586a090f94f695c5/4abbf/sdlc-harness-loop-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 13.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA+UlEQVR42mPQdkuQlTP2tVa38HmkZx/0U87Er85WXzTbwUzpp52J/GtzbdEgBiCQs9VTVLDQ2aPranFKwVJnvqSVpq2mg8kLHReL99LmOl6BShlihuouJgxAg67Kmvj46tj6fzP3iP6vZBHQY6krXu5ipfbf0Vz5v4WOWDTIQHlLHQ0lC71PBm7W/xXM9c6KGqu4adub/td1Mv8vZagZbqbuvsvVMvw/g4Z14CEN92hJPe8kHTXnaFOL0ELpCCdFcU8rJRNfG0U9NzcLFTMvd287b29Vj1wPduNQF36fNB8us2gPPnk7PUN5V1MDAw8PUQttz9V2xv4fAF/7QyuE+1CjAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Horizontal SDLC harness loop showing six numbered stations (domain aggregates, SCNs, API contract and C4 diagrams, SCN-driven test generation, CI verification, and a sharpen step) connected in a cycle and grouped by the five states they represent in the scenario lifecycle; each station names the specialized skill or agent responsible for it&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/9824f63570ce6833586a090f94f695c5/3c492/sdlc-harness-loop-dark.png&quot;
        srcset=&quot;/static/9824f63570ce6833586a090f94f695c5/ff46a/sdlc-harness-loop-dark.png 325w,
/static/9824f63570ce6833586a090f94f695c5/a6d36/sdlc-harness-loop-dark.png 650w,
/static/9824f63570ce6833586a090f94f695c5/3c492/sdlc-harness-loop-dark.png 1300w,
/static/9824f63570ce6833586a090f94f695c5/d544a/sdlc-harness-loop-dark.png 1950w,
/static/9824f63570ce6833586a090f94f695c5/d2433/sdlc-harness-loop-dark.png 2600w,
/static/9824f63570ce6833586a090f94f695c5/4abbf/sdlc-harness-loop-dark.png 3088w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-mobile diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 612px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/d83eea0e5c00c148c5799ecaa11cb2c5/8c76f/sdlc-harness-loop-mobile.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 351.6923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAABGCAIAAABDrSOmAAAACXBIWXMAAAsTAAALEwEAmpwYAAAIUklEQVR42oWXB3PiyBLH/f2/w3v3qu52b4O93oTttdeJYDKSyAIklBNIoJyY0WsZ35bNgm+qixrN8KN7uv89lo+yl2OkIFJAfQn1ZTRU8VjPaCPbfg5k1JMQISDWwPBNjLOj5yQ8UyIaKJjWMMEF9al9S+nXhFrsLW4IjeDCiZHBj4KD/fBAQbSO2WXWmtn3lHpe586q7PH14OR2RM69uZmNNTTRDsEygm2Ay4NFocpeNfk6vbxqCWc1luIDZpmNVES/CmNmiYV1VqOt6458R6kXda45XXOrbLbAIxUfhOFIcLAO4wB539Wu2iLAV638szV1BlIEP02rh+HpIqsMFj+awo8GXx4Ypb5xQyiXTeGizndYl4bMq4fDpnUk2lmlr5/XWAgYTlvu64UHBg7Pr7PRKwmDIlMShmpRYkrySYeLSSHZGqxALUgRD+V9MAzDwaKVsXrMGamyzuRVbpKFVTsjpiajRpKVrTy8/fIRznYHxjiMQvjcWUcIhWH4fOVoG8Avg+F6kblyYLLZYISyrcEWQtj1w19fy2HLx7mMlSeDZFJC3OFCyPlA3sAKiBEkLa9ywvNfepYsNFAxVH+igzayrhDkwuxqjckaHqcGnhkYajvV8yR5wUtYWeHhEwxO0trYKvb003v6Z1uEmsHiFmYeO8kPouepOJIf4amBQI8lqGd1fksqIGaw6440t3JJAjzTH+Ewep7IHIbqQSdUhkuQVIdx7yjtrDYv9XR4vCVViBz6bAsHYYR2YRUzC+hB55ZQ6pPVw3B5T2kQf3VkwiNE/hq8PTOELTrZHaVAJ4HBmSd6Ap0EJ/oV9i4MCcsvGjWGpi88zK5aEKoEqr5scMWueno/IuYOY2bMY7Z3YShVi8eUhBpM1GDCJhuBPc6j1jyBOSmijoDHj53k78B+jHlofRNLqycl80s0N9KtpGFRtDC3xCt/X9igzpeWpWkSxdFW5jtbu/Dufh5bvHaDvBPw7tZvcLRAZhVZdWTWkdXInHawqK7Fu8ztoFUzX7cayHzA3nwPjJwptq6wXcROcWPdhOr5ijmJ9B+u+C1UL2Ax31pfI6v1CIcvYZfF6yJ268itxWZxMf7EVv/jiD+0wQebP8/CFmxhp4xW5F7PDLbvkVNd0Cd8839q9299fGIyX7XBe4M+0frv01UZuwAT+zwDvL7HXs2afZE6f+qDD654YU4/K9RbmXxjjI6R8/DM8364DvCS/rRivjh8ASbG6ONycrpiv2Gnehh2nzxnUStLCRQ09OFHlXobryvZhsziNmxhp3TYs11KVhWZfJsb9Tc4hInU+WtJn85r//Xkq8yr/nPmnYTZUKqfm9W9LxegNoFyHqjnnlQIlLNIv4TJxrrF6xtk7isVjixkNtGqk9kktsnMITerTmq1M4fCawJW8IrAZhN7Yg4HIdyhL67enZGkaRjF2T+SzHX6pFDsRdEmp/HW/xHGz6/tfClOkyCKHrWdt6ETh2cT4qRbv2C6hQl50m18HbU/UrWxJu/xnKZp9MxzipARONLakjxrvl7MVwveXoLZUXC0TkMxXMuRnVtoq4lLmxqlCFriSkG+rkSOFrurNNyT7bYrlXymGQr1kG9GQjMSwRqB0IrFdiw1Qh6sFnJlh4UQIBcvEkb4UjMWRkgfIu12MfwwLr3p3X6gy2/6d+/HpW4sj7BOpXLN5xK02YU7ntRKxTtjeC4RZyJxYwzORQLgT0zt06wGi0VzDHDVAxjtgxOxbNHvxqWPkwrA4P9kVj2ZVk+Z2vHjCpUqDwdgGY40yZbgcMsUhE7L4z+zjVOmfiGTA6QTibTfM+HJjYgfbLS7xeh4WoXIP4zLcFqA341K8NhPVTKRanvhpidWojmZymVn+nPRv7PGBalzqfduzCE8luxJJ5WaiVB02T3wLFw2PYHy5W6g9iK9F2kwIX0FJv1I74YwlyEvPV8FCJT38t7OZbmVaK5Y2ImSJAy38vwl3a1w993bOyOOk+Dl68Ov8ftfjEyJbMpV6GAxDoxJbLZNoabNZ4k1DmFl0fe0abA8COcKDZmax1ZcpuLMqj4LaSvZU7CazzZi/taebftkD0x5SjMR7/Thd74Nkno7uPurd/tn7+YP4uo71+5ulLIzPwiTUOpYqK5nIJKv8+al2r2QyM9sHRRWWU+pjVxy2Nc81yO+7fMgz+Ppw5d5oyC0v/MtmNcc5t/hWsSTkfiVa4K9GdxB/Gdi5wNdqTnsv8E+wFwvUb7xrS9sA/xfyl0IG7qKCEUQdvkZjHfgrq/UQ26QqtBAJ09hg9tygW93ExmEXXG3CcMBvMTtvLi2PakYzMhUgtp8kzoXWvdcpY7ZKsi7HYv1mHsqFcZ+8NtbL+0vSjbbdIWWL5Gx2g7khieSsZZPXKHqcE1HPAjv3ttxGoZx9vzifnqtxq4XvHj33B7m+YtH7iEKcLar+k3ektFrnkM3dY1UmJncZOEtN7Ye2UZs67GzSAzRs9TQ1uIkQrvwNh6D9fmmI5Gu0HHAuNaab9t8Kzex48iUN6+t7UW8+w/KFl7ygdLzzGmkDrx+UakWmMt33ZvjQfuKs9nUGAfwu675lJFdeMEFWj+gH4zmxbxxztbOGOKnCHPip1A/Y0XSlkjPWR6G1a7PtVetHxxY5eu0f6/ApHnBUTeS3HUkwj0M84FMuuYkZhpL8loEb+1LvlaYVb/PJMpZ0pHQdtzXPUN4xVO6/GUCPDiEsB++zcD+zTPAPV8g7IfvM4i5fcWPK3r566T4mW5dzPWhL3Zegee+Qnly1+3eSJ0rYVTSmIYJB4a0jcv6YhQKLcdZHIBXSij1XHXkGZMQTKcDLb8Wn+bK0BO7jr9OtvD/AYV2HxhPt9btAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Vertical SDLC harness loop showing six numbered stations (domain aggregates, SCNs, API contract and C4 diagrams, SCN-driven test generation, CI verification, and a sharpen step) connected in a cycle and grouped by the five states they represent in the scenario lifecycle; each station names the specialized skill or agent responsible for it&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/d83eea0e5c00c148c5799ecaa11cb2c5/8c76f/sdlc-harness-loop-mobile.png&quot;
        srcset=&quot;/static/d83eea0e5c00c148c5799ecaa11cb2c5/ff46a/sdlc-harness-loop-mobile.png 325w,
/static/d83eea0e5c00c148c5799ecaa11cb2c5/8c76f/sdlc-harness-loop-mobile.png 612w&quot;
        sizes=&quot;(max-width: 612px) 100vw, 612px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-mobile diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 612px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/999c897a4d8b94b4581bef690b555023/8c76f/sdlc-harness-loop-mobile-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 351.6923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAABGCAYAAADMz7TxAAAACXBIWXMAAAsTAAALEwEAmpwYAAAL40lEQVR42p2Ya2wU1xWAbUiatDEB7PWu9/1+v9+7s7Pv98Nrr+31G9vYzgIJGENDMI3SFaTCCQlBDYQUkdRpo1bBakSSClTKD9pKzS9+9EekSFV/Var6o1WD2qiE3Zk9PTPGxPiF6Uhn587cud+cc+45587dpqaHj23MjyFz6LgyUP69kih/Jvf3XZN4StdEruI1gaPzwVnm672Gz/xaRfbfMvWe6GdHl8vbV/Ga2BvayPgVmT0FYnOsrvXmwRIqgS+7BwKFCXCnhsCPbea+yBSmZI4MGPKHTzDjwuHwE+sClYGBX0rMERDoA3eNRGfdmx6qx0pT9URfpd45NlsvjM7U9d5cna8jvpFaYqBLVY43LRHXB6rIoSsSSxRwQN2V6AcEQbhrL1jDPRDsHId473Og8WRAoCMoiRWByX0ntgTEAfV2lQesaDJZGENTRyDWMw2mQCdw1V4Q6gNbBAaHrqB/wOAv1G3hEqDJrGb+zAieJ1iwiSyC0h6nxPjiLQGFxhA44+U6Y2qkewoY0xnxZ0fxehKipWl8YZ4Soa8N6UeaPPiRFN/crvTU3In+BvqsgZPSSODZnRxooE8btlA3zVN56kuTsn9uU6A2MnFF7syCxJqoK5wZULlzoPbkQenKLok7C3K8L7UlKLkrD6ZNwqaZ+XFPXzCae0/GQxOnc97hk1ld94mwsedEdEleiaqLx0PdM29VYlPzncaek1H/8+9Jl4ZD8zJk2yppBoDm27dvf69pgwP7t92+/bcN+9ccn3z5jx0f//Ev3PuDm1f2lTHFUL5TfeMi1+msPFmtVrc96PS89LFIHZu8qArv+Yk6MvauOjLxrpQYuqCLjn+oj439Sugtn9dEx5f6omPvqCJjC7bR1whmbPXNX3DWaBI48pEVnU+LMUzEpnBDaCAxC7JgJrvAlx5m81hoCDJ9gM9QUmscjF0vTS0BL60Feg98YBFb4jWRgWyIjEFKZo3SVgwJDA86O3SQJjvHabEpxIrIEKwxGWQqHp1kxs6dPt+2PBHfAmcYYAwHBIGjdDfciQFI9FaAyI2yWREqTmCVGYMOjQ/wpdRKYLV6ufUh/y0DJQgU6AlwxsoNJiswA1hzYz3PgTs5yGYGk26MBWLMjJXAxcXF7etqiInOJH2D0QxNBkesDzypQbBFesAe7UVfdqMfQ6uAZzcB4mRgZWlwlC4sAsNsdQnk97BmMxPGw+rCaPiwyesBcVKEhnBNbolgrlaoZF+FDhcnaCI7Qsd6puhI917alx6kCyOHaL0/VxPoyc2BTNjIXZ00k5taoruhI7pB6+9aEoKRblB7O0HjL2L+ZimFuwCW8stTG/owfPjqLkPxaFmfO9SvTEyzoopVyvrMgVFz/oVxeWy6T5PYh/f39WszB8u4gI14pt+Ws8BzC7vWADc6qgsLT1+4sNiy2TOvXVrcuWXgz2/8/ZnFz//aer8SNG8Z+MERtXAg1HG6l0TBc4nsmC94dp8qBmUXB9KmD7LOHSe7CO482x/k/wjl7Nyg0MuMnZ+/uHsN8HxF4XIbuA2Luh0sak7DoecBYZdAKqABv0UIEbcC/DYxmNUcsGraKYeOC5W8+MCGs/zmtNKJoLpO1kbrFW11u55PZUJGarSboEingi7ELFTQraTU4p2UXt52z6TkwERSuG9D4FuTCpdV3dYw4IPZsLHRn/dAV9LGtCHu10AxYWXbzNmi4VImZRtMpgX7NwS+zgA1nAa+HRKEtlHOuaAzbgHSKYckqYeetAPFCfmoGYwqDmVScWB8K0CdrBWSAW0jw2hGaCHkVkImbIACglJBPcRQW7TiMYDyVlAJn23IBS2gkeyCXMQEpbQdzCouyPjPgEK4A/TyVsr8KCAzKWZlG2VW8xpoGtWbcdKllINOBw10Ke2g+7IuOh0y0CMlgiZsshpCYTyxCZAJG6eO07BquRB0yhqETQIBDBsmdPCabfssIrbt0PEoOz5XySyFzdzc6TYssE+sWIKbmy7PyHl7U8LZkRh/ti/Ame0PcmfLIc7hgajoxYGI5Hg5wDncH+bO9gU5s8Nx/sxUUnzkjUmFiSGcO3cVcxkeI5cXb7Ws/AB4sIa3t7e0tDRx0l1dMpVK1d70bFNrC7+FWbCeZtdcp9P55EphPkkmjx3bMTc/v5tpZzKZpxhaqDun9+cz/4719dTdmcR/ffn0XWzfDXZ3fh0r90Dn2NiBrVYbVkNPxvOsze8ZItLpCWsksM+ViM36Ctn9rky8Yk+ED/rzeVOT+eKB3Za3D8qdl2Yk9gvPS63vHpZpz08KypfnnXvfPxuWXKrwvQvHZUyf650DYsMXZ1u1X76+g3nBjz+8/uyaWRbnPKc67BrgWVT/7LCq7vDMyjtcs/IrrlnxHzx/3W5SfNVulN3hWZR3Omyqf2F/Qxix3WDGHjlSba1UKk+u+D5qbhIn7Gf4ZiW0KgS1NqUQ9AEHBEspiA8UgOxONqL9efaap5NCq1JA8fRSEPiNf1iKw3NrK7Y45TzDNcpAF7DXrAk/2JIE6IMOsMb9EOhOgrcQZYW5VvssFBfBgoDpUUA5KL2mWqg3w0J0pAMXJxv4i3FWvPkoEMUE6EkH1a6VbA4UJpxnBFYV7JTyajbU0NcZYyGOVAAEZhW4c+H7GvrQZCHFM8g2B4qSrtf4OLBNLbqr9dsooitBodkU+o0K9qQpTy5CoeaULUFQHLXoHk+PQML4uw2B0qz3jBBnGX1Tk7uNoAs6Qe23ghV9aQi7QBuwgz7kBLnHCKgdxTcpQBS2bqyhbE8iLcp4zoqT7pP8mO3V9ojpVV7EeoobMc8L4o7X20PmU+0h06tMnzjlOiVGF8n7gtOPvS4v3Lr19IVbt/6vdXn5Q/2hY3Hx8+8uLt7cuao4rPzI33ih182UTIrB5LioSA6KugLDHUV/2TpafME50Xu8NevqZe6JSuSgsj86pqwUo8vgDYGygq/Kd2hBZNPWJU49SFFEdi0o/RaQ4UTJ3Abs01AdFhWIEo6by5ukDYGSlOeHTLZg2t1zpkjaFvfTkb4sHe3P0fHBAp0a6aLxfo0JamHYcuPRwIynisUAP9cMdSao3dkQGMNusMS8D1IPQ4pqx7QThqw3twTEagJCs6pOlpKAwY3QMNgxFh0pEjMnAVKH7jGAKU+1A4F8g7zuyoRYGFNlHGmShZJYbaT2xwViPnO1krozHcRiEGGLhDniAQ+2fYUY8I0Kikm71cA12wo2n9PuV/g4gxy1+C5WlDqaXHfnInV7iqiTPem6I0PWMTW/4enlDPA3y3+pnFu4umtVjH4bNkKXDrh6aZ3JZ2uKAGPUA6YoTkoxBuqADXhGOcVUJUnCeXN5b3z28mLruikk2xPvkqRci+K4/T1+yLLQ5tP9lEeaf4btD3djm0uaFsQJx/uimOMjWTl8bHlHuiFwvePP168/dePGn55Zp6v5/vZ2O7O9ve/DVWZXMTfDTU88EMxVZr1e/GLj4rAIsP2zTTboTSu3+N8fviAdIGfi00PVsf0jpybL5KHwQHQ2VQ4dTQ7GjiZ7yaPxseIPSkOZY7mR6FyqOr6wa50i8u2fECXy+RmbMgZGKUmZ5aEGCliVEbAowqwwbea+QULQLm0aJlJVYtmv6wKznqmDWqEHxBzTNyqBs+Y3Z2rpQH+tnJ2ulRITtainuybYravJeJZ7RmkAhuIv+pc8tyoml4EFf2VGjUCXPk4lfL2Q9JchRfRD2NkFCV8fRFxdeF0GvdTXQCtgNPXykoZNG2hY8E/NaEVeMMoDVNzbAzGUXGgYArYcMNdxfEnQ0QlasbthlAUfDcx5p2b0Ej9I2k2UXRNFzYqsllFPCdKBAciQg6CTeEHKNaN/wwh8aWsaGmR+qhAdhUJkFMJoZgjBcW8vq202OAQa1NC0JQ1904c0QgZI1LLBQRoBdNSNf2wYk2y7GBuj0WxawbdTJoyAPem5zSelSO5HDX2gEbkp0l5gtfMYU2BVh1kNmclhruU8632TX14/bJZL0XBqrhC09PyWMHZedWuynzLi0eU+9ekLnzrVGVaYa7+x+EnE3n+9UnhTt6zh/wBz6bg/qQ1GTgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Vertical SDLC harness loop showing six numbered stations (domain aggregates, SCNs, API contract and C4 diagrams, SCN-driven test generation, CI verification, and a sharpen step) connected in a cycle and grouped by the five states they represent in the scenario lifecycle; each station names the specialized skill or agent responsible for it&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/999c897a4d8b94b4581bef690b555023/8c76f/sdlc-harness-loop-mobile-dark.png&quot;
        srcset=&quot;/static/999c897a4d8b94b4581bef690b555023/ff46a/sdlc-harness-loop-mobile-dark.png 325w,
/static/999c897a4d8b94b4581bef690b555023/8c76f/sdlc-harness-loop-mobile-dark.png 612w&quot;
        sizes=&quot;(max-width: 612px) 100vw, 612px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The six stations on the diagram correspond to the five states a scenario moves through.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Analyzed&lt;/strong&gt; - the scenario&apos;s domain aggregate and SCN exist. The domain skill elicits the states and invariants, the scenario-author skill drafts the SCN, and the team edits.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Proposed&lt;/strong&gt; - the contract-author skill writes the API contract and the C4 diff against the aggregate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Approved&lt;/strong&gt; - the &lt;code class=&quot;language-text&quot;&gt;/e2e/scenario&lt;/code&gt; slash command drives the Planner / Implementer / Reviewer / Runner agents to produce the pytest file, WireMock stubs, and &lt;code class=&quot;language-text&quot;&gt;docker-compose.yml&lt;/code&gt; updates. Each agent calls the relevant skill (&lt;code class=&quot;language-text&quot;&gt;e2e-scaffold&lt;/code&gt; for the suite skeleton, &lt;code class=&quot;language-text&quot;&gt;wiremock&lt;/code&gt; for stubs, &lt;code class=&quot;language-text&quot;&gt;e2e-run&lt;/code&gt; for execution), so the project&apos;s E2E conventions live in one place.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Implemented&lt;/strong&gt; - CI runs the suite. The Runner attributes each outcome - pass, fail, flake - to a cause: spec, application code, test code, or infrastructure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sharpened&lt;/strong&gt; - the cause gets fixed at the source. A fuzzy precondition becomes concrete, an ambiguous endpoint gets a paragraph, the aggregate gains a state if it needs one.&lt;/p&gt;
&lt;p&gt;One honest caveat. The Sharpen step is not pure automation. The Planner surfaces gaps, the Runner attributes outcomes, a human decides what to write. Outcome-to-spec attribution is a classification problem we can in principle automate further - we are not there yet. More on that gap in the human-in-the-loop section.&lt;/p&gt;
&lt;h2 id=&quot;the-data-15-suites-162-steps-and-the-shape-of-the-curve&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-data-15-suites-162-steps-and-the-shape-of-the-curve&quot; aria-label=&quot;the data 15 suites 162 steps and the shape of the curve permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The data: 15 suites, 162 steps, and the shape of the curve&lt;/h2&gt;
&lt;p&gt;Three months after we migrated the first suites to pytest, we have 15 suites and 162 test steps committed. The growth was not even.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1000px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/4d42c8ca18f152bd90554c53608e163e/00d43/e2e-growth.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACA0lEQVR42n1TW2sTURBeQdAHpSiKFNE3X9Wf4JuCKPokFoT6L0RRmpim7bZFBRsULFJjK6lUn7W0CWgvNEh8r4nY2K0t3Sjsnk3O3j5nTtwQ15iBOWfmXL75zswcDR3EEg6qxg6MbRNbpNu7P7G1Y+L7j12aa9ikWTiNP6fD1r0wDKF5nocgCJQTKftBECqN+8rmNVKPbKVs+z4YS/PJ4EvtEnZg3Q7gk7ZOBZIHFYyxFGCcYVyj/faQjYZAZukD9MV5jH9cQsU0+c2dGcaZsaxVq0jm80gVCsh9/gTLsXBybBx7BxLYN5DE/Pq6OqdJKbuycykgy6PlFWi372DP3Xu4mH0Jx3VxNpNBr67jxOgYCpUKMQygubTRTTyVL+Dp6jIOpYdwdHgEfbOvCVDi9OMJ5feO6E1AZvhvfprlV5UkW/oejT6erK7gYGoQhwn0Wm5WMewKGOlfdQ65FUSTYbGInlQaR4aGcZ0ABaXqzEQGxwjsuD6KQrn8H4aUBynruPXuPW7MTKEv9wpls4ZnxTWVv/2JJK5MzyiGpx48xIH7KfQMprHwJQYY0NM8UQNcB426jf65OZyfyuLc5HOUDAMLX7/hwossLlFBEot51Anw5pu3uEz+VQpQ2jSagBE7bh9ZFy2i3LyljQ3YQtC6A1ktwrZ+wbLtVuPzGe4S/iHR1/sN6htc4Zi/O7gAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Bar and line chart showing E2E test suite growth from zero in February 2026 to 15 suites and 162 cumulative steps by May 11, 2026, with the steepest jump in April when the harness emerged&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/4d42c8ca18f152bd90554c53608e163e/00d43/e2e-growth.png&quot;
        srcset=&quot;/static/4d42c8ca18f152bd90554c53608e163e/ff46a/e2e-growth.png 325w,
/static/4d42c8ca18f152bd90554c53608e163e/a6d36/e2e-growth.png 650w,
/static/4d42c8ca18f152bd90554c53608e163e/00d43/e2e-growth.png 1000w&quot;
        sizes=&quot;(max-width: 1000px) 100vw, 1000px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/fb9d89250d64003e3f134dbff5c18a33/e8950/e2e-growth-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACTklEQVR42n1TS28SURQeOtCSYm3TkFjmDmJMndZH3KmLLtiYlpU7duyasDAhRhftRnODBph7BzqMUHQskZWbWZmutF2MceHCYExsQ42KGDSN6W8wPd47MBRm0ZOc3Nc33/nOYwQA8Al9c/fdbne29aN77cvXzmKr3VY+7X+fPzj4ubD/7deVVvuPstc5vPy505kZ/oaZD2M8JmDLGrcsS+QPrvOHYU8mLdF1jGFwL2DbL1ggJgHEtGkGGg07KJhsM6zSE3Ww5wRx2/Zz58TsxbmHtBAApi6ZTIqGYUwI6R7h2LBCr3Myb9A6uTO1UMg+VNQslVSKLxJyycEwsN8LHjGeGrPzlC4hommSWlQXCzi9WlqdRZXqEXr2HORqDWS9suLgq5Z95jR16WYzwHGSUXkQrb+EqLkFqLq5E8F4UiL0EGlFkKj2L5qnywwvCtjcnjyN0Ck8s3n1yRrSy4BKG8CI3lzXtBBbj5wzI5VVdaWfEfa7NfLWjXXElzAyExx3gebWkPEUOKlE6Y6XMJanCYewNwonhCPdZt2DTMIhnCPkvlw2AG3oTKH2tk/4FxVLPOXjgULebj6U7nByEk1LhSJEqynq43cxUtgN5/NKrFy6GzVfgLxZA0nXnRoytUdypQqIBZJ1/SRlR52N/TZWwoCF4Pr67WlEyLZEis05QvfkfP7GWWosRwj9iFTygXc73mgE2fpa5meVvmcKb44MLlM6rt+LzwzUskBXs4+WsPkqvGuZ07+zU7fqBEu53NY5tyT8D0ulUqFMpldnzvUfCWCOTEE50gEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Bar and line chart showing E2E test suite growth from zero in February 2026 to 15 suites and 162 cumulative steps by May 11, 2026, with the steepest jump in April when the harness emerged&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/fb9d89250d64003e3f134dbff5c18a33/3c492/e2e-growth-dark.png&quot;
        srcset=&quot;/static/fb9d89250d64003e3f134dbff5c18a33/ff46a/e2e-growth-dark.png 325w,
/static/fb9d89250d64003e3f134dbff5c18a33/a6d36/e2e-growth-dark.png 650w,
/static/fb9d89250d64003e3f134dbff5c18a33/3c492/e2e-growth-dark.png 1300w,
/static/fb9d89250d64003e3f134dbff5c18a33/d544a/e2e-growth-dark.png 1950w,
/static/fb9d89250d64003e3f134dbff5c18a33/e8950/e2e-growth-dark.png 2000w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;March was the pytest foundation: three suites and 33 steps, written by two engineers who were also inventing the conventions as they went. April was the expansion: nine new suites and 83 new steps, with &lt;code class=&quot;language-text&quot;&gt;SCENARIOS.md&lt;/code&gt;, the &lt;code class=&quot;language-text&quot;&gt;e2e-scaffold&lt;/code&gt; skill, and shared WireMock stub patterns emerging in parallel. May, through publication on the 12th, added three agent-management suites in eleven days - and those are the ones where spec-first discipline paid its largest dividend.&lt;/p&gt;
&lt;p&gt;The clearest signal of compounding is &lt;strong&gt;commits per test step&lt;/strong&gt;: how many commits the team needed to land a working suite, divided by the number of test steps in it. Here is what that looks like in the git log.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1200px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/00b24059d74e58f1711dbdcde6e02f3f/c1b63/commits-per-step.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.153846153846146%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABeUlEQVR42pVSS0vDQBDOP/LsRdCDB0+iR0Hw5u/QX+BFRPEg+EBEELx48IFI1Qq1evBVqzZSbPPYJLZNGpPd/Zw0TUnEHjrLx2QmM9/Mzo4ipcR/IoRE3bChMyeGGUMjnxAC/UThP224dzsQgYeIWlJwlJAuJGRsp5HEJDqBwj3qYmkIwjXj7O6PtMjoSGT8yfffWCX0HBgrw0TI+hP+k9yX0PvWYRJhaL5C8qAXJKXo9DYwYdhiMFdHYO/PwTmYR+N0EY2TBZplmOlY9grR3LpICEVmhq4FIyLcmwVbGwXbmIC1NYVAPYIMmoN3GD1KNEN7dwZsfQxscxLW9jS8w3H41jN8unm91cJyuYwXxvCk67iv1fCgaWj6PoqWhXzkdxyUDIMIAx9u6Rht9Rpe+Qze+wVcQviVg2HX8WYyPFLyeV1DoVrFZaWC3EcFN+onimTfEsmVbqBAhfKqCiW9T9EsQtKcEHAJzkW8l5zTg/GO7sSndjVjA/gF2S335VA/SaQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Bar chart of commits per test step across all 15 suites, showing early suites averaging around one commit per step and later suites dropping to a fraction, with several suites at 0.05 to 0.15&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/00b24059d74e58f1711dbdcde6e02f3f/c1b63/commits-per-step.png&quot;
        srcset=&quot;/static/00b24059d74e58f1711dbdcde6e02f3f/ff46a/commits-per-step.png 325w,
/static/00b24059d74e58f1711dbdcde6e02f3f/a6d36/commits-per-step.png 650w,
/static/00b24059d74e58f1711dbdcde6e02f3f/c1b63/commits-per-step.png 1200w&quot;
        sizes=&quot;(max-width: 1200px) 100vw, 1200px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/a0b1eb4b1f9323fab1b808bfcddc775b/97a96/commits-per-step-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.153846153846146%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB00lEQVR42mMIDV3FzIAF/P//n/nSzYdK9+49k78KxNfvP1e4fv25Akhs//79LAy4wIVuV+5nTbwx/3stOCEG1TMBDWOsr69nAmEQG4iZIHwgDZSvB/L/QzAjEg1mM7zvlxf43MZ94X8brzDEQAawJJprYRoYkcWQaTj74TRdwWcNnGefEDAQq2ZsYgenJsg+r+MAGaj2f6YxK9zbq0KZQYaTbOC5VhvRZ/UcJ57Xcy15Vs8573kDZ+PTBu7mM2kMrCguhnkZhKHhC5IHhWc9UjgyXGvTEH7WwHXieSPXsrdNXAc/tHJv+dDCvfbTQqWgd6uU+El24aN6GaHXjVwn3zRxLQPSB182cG5+1cS14f965ZOPVhnrghTaLl8ua7l3b6dyX5+2eH29jlxPj7F0e7u+cGkpr9WBA7a2Bw+6mWzbZijc1KTOcKw3lPN+s4zHg1ZJ63vNkm73m6Ud7rcqO35YqulSMbtQRbpzuqretDmGDvv2BWtPnGil3tvrqNbZ46zR2Wuv2tZtbrdlh6PLvoPe1us3Oyi2ttoyrFq1ihkUBqtW/WcG4fr9/1lW/Yew9wPZ/4GJ+P+qK2z/ger+zzzDCuED2SAarG8VMwiDxf7/ZwQAnL5R6pzipOsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Bar chart of commits per test step across all 15 suites, showing early suites averaging around one commit per step and later suites dropping to a fraction, with several suites at 0.05 to 0.15&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/a0b1eb4b1f9323fab1b808bfcddc775b/3c492/commits-per-step-dark.png&quot;
        srcset=&quot;/static/a0b1eb4b1f9323fab1b808bfcddc775b/ff46a/commits-per-step-dark.png 325w,
/static/a0b1eb4b1f9323fab1b808bfcddc775b/a6d36/commits-per-step-dark.png 650w,
/static/a0b1eb4b1f9323fab1b808bfcddc775b/3c492/commits-per-step-dark.png 1300w,
/static/a0b1eb4b1f9323fab1b808bfcddc775b/d544a/commits-per-step-dark.png 1950w,
/static/a0b1eb4b1f9323fab1b808bfcddc775b/97a96/commits-per-step-dark.png 2400w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Suites 01 through 07 averaged about 0.78 commits per step - the agent&apos;s work while the harness was still thin. Scenario format in flux. API contract gaps still being discovered. WireMock conventions not yet shared. Suites 08 through 15 averaged about 0.17 commits per step, a roughly five-times improvement. Suite 06, &quot;Re-auth DCR reuse,&quot; is the most striking single point: 20 steps, one commit. The agent reused DCR work it had already verified in earlier suites; the SCN corpus itself was now part of the spec.&lt;/p&gt;
&lt;p&gt;There is a quieter, more organizational signal in the same git log. The principal software engineer on the team committed &lt;code class=&quot;language-text&quot;&gt;SCENARIOS.md&lt;/code&gt; alone first - a commit with no test code, only scenarios - and then delivered three agent suites in a single implementation commit afterwards. Spec-first separation, visible in git. A software engineer on the team writes scenarios and implementation together in one commit rather than ahead of it; growing engagement, not yet full separation. We did not mandate the spec-first pattern in a process document. It showed up in the commits as soon as the harness made it cheaper than the alternative.&lt;/p&gt;
&lt;p&gt;The third chart is the one we did not expect.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1200px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/ee744abaf038d021c45bef35424caf5b/c1b63/ci-time-flat.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.153846153846146%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB4ElEQVR42m1STW/TQBD17+OI1BMSFBCBNqXcQKJQ0l44wJVKSD1xgyM3xIUDEJTEcbCdrzZxCm1jUEocOx+Nvbt+zNgxVG1HGu36zbw3s+PR4jjGRYsRRgKDoY/fJx7cwRCDP6PkzmcYRrjMlFLQLhdMg1mMz/MuyVXGZUxJCCGgMVGcIWcCakFkk3HqCqmfb0FFp4jCKaSkDk9mM6zoVfQmkyQ4kxKRUmkiCXIxYlDynJQifHL7eN5o4EP/GB9dFy9bLezut1GoN2hMEbQf3ghXPn/BTqeDV/sdbFgWXncd7AU+3h442LZMvOt18P7oEG+cLl40m7hZKuNWuYyHNQM3SiU8Js51wibTKbSj8RhLxW94atm4Xa7gXtXAxvcaljnRtHCnohOu45Fp4hqRnlBevlrFA6OGrXodOYoX7HrCnYchtD6pXv1axBaBeQqu6To2LRaqYJOwVSKvUxEm36XRZNiaYdDdTgo+I4y7Tn6KSzNcKhaxbVvIE+E+kQtcmYXttJsEo3tuQWZs3TCSznLE4XzuUND8tVNSNYdDOPT09sgnH8EJAjQ8DweE7fkp1ptM0QrGSazt/89r0tldnLwVF/Yw+xYqBs9XhHNIoSC9n1DBL3oWr5j6t49nOWx/AX9gzgMgxqrFAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Mixed chart showing CI e2e-test stage wall-clock time staying flat at three to four minutes from late March through early May 2026, while cumulative test steps rose from 33 to 162&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/ee744abaf038d021c45bef35424caf5b/c1b63/ci-time-flat.png&quot;
        srcset=&quot;/static/ee744abaf038d021c45bef35424caf5b/ff46a/ci-time-flat.png 325w,
/static/ee744abaf038d021c45bef35424caf5b/a6d36/ci-time-flat.png 650w,
/static/ee744abaf038d021c45bef35424caf5b/c1b63/ci-time-flat.png 1200w&quot;
        sizes=&quot;(max-width: 1200px) 100vw, 1200px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/a30c9570534bbd4a5d7f2fcf989ea214/97a96/ci-time-flat-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 50.153846153846146%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAACVklEQVR42l1SS2sTURSevqK0delCF019gVoX2vqauTNJqmkiKO4iXXQl+AfElSCzKLrQVaEggYoQm6neVcXQyczcx0wekyaMWCSiFvoS7MI/cT03Jbb2wuF+93znfOcM8ylCiB7l0DFNs7fgOEPtjY2RH9vbp9Z3ds5829yNf/25c/rL961RjPnw4T4hlB7o6+80HxaUxSbn/R0OQkCYBwJj3Nfhhejtigmc65ubWzmiSOIixrGusBTrCEJxdwuJO83y/d9mezjK3xtcWHh4LB+JAeX+8vJJlbLKDde9IMmptbWhO+swCU4ONslhMyZySp+AW+bSrv1Ap3QhSZwZnZBpwK8nnZXnKmHFV4XCkDJt22MoDH9pvv9CJeSlRhm+ydisQezxlGc/1j2vmC6Xnuiu+yjp2M8SFX/eqNfDZBSxVKOBE41GE9VCS6+Frbk3K8eVu5/ccyCyYVRrBSADvVqlqYB/SFSrTQMKUa3uG82IGc2WZbSiVRRULY1yX2O+hyhdRDwINUIsjbBoHuNhRaU0jvxgC8h3ukcrBiE+fMp7xHhTI76lMRYgygnEIuRCGL6kcT9AnDPE2BLi/iqIW8B9NnE7pqilUhyIzQRxFzVKfcBU7UyGZsIsmYONCAgXYejqvxxhVOZgQANwEXBkws9VZsBvUw5DWZuOZQkZzzhsIkvpWKZMrmd4/XyS88u3IJ8ErHr1K5PAyXfada9KfNvzrk05/JK8xZ4L9m0gwAZd+0xE0UDyY/ns73x+sN0Wsd3Z0fifpyMn3nJxdN+X0odgswNe/gvYZn8piXR0WAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Mixed chart showing CI e2e-test stage wall-clock time staying flat at three to four minutes from late March through early May 2026, while cumulative test steps rose from 33 to 162&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/a30c9570534bbd4a5d7f2fcf989ea214/3c492/ci-time-flat-dark.png&quot;
        srcset=&quot;/static/a30c9570534bbd4a5d7f2fcf989ea214/ff46a/ci-time-flat-dark.png 325w,
/static/a30c9570534bbd4a5d7f2fcf989ea214/a6d36/ci-time-flat-dark.png 650w,
/static/a30c9570534bbd4a5d7f2fcf989ea214/3c492/ci-time-flat-dark.png 1300w,
/static/a30c9570534bbd4a5d7f2fcf989ea214/d544a/ci-time-flat-dark.png 1950w,
/static/a30c9570534bbd4a5d7f2fcf989ea214/97a96/ci-time-flat-dark.png 2400w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;e2e-test&lt;/code&gt; stage of our Jenkins pipeline went from three suites and 33 steps to fifteen suites and 162 steps - and the wall-clock barely moved. Every CI run still finishes E2E in three to four minutes. Two changes did most of the work. We optimised how we reconfigure the environment between suites - re-prime WireMock and reload stubs, do not restart the application - and we reimplemented the test runner from bash to Python, which dropped per-step execution time substantially. What dominates the three-minute budget today is the application Docker image build and startup, including database schema setup; that is the fixed cost the growing step count gets amortised against. The next section is where we cash in on that property.&lt;/p&gt;
&lt;p&gt;A reminder we keep repeating to ourselves: three months and one team is not a controlled experiment. The dataset is small. We were the variable that changed the most. The numbers are visible, but the trajectory is what we were trying to see, not the absolute deltas.&lt;/p&gt;
&lt;h2 id=&quot;the-docker-image-and-harness-build-making-prod-like-the-default&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-docker-image-and-harness-build-making-prod-like-the-default&quot; aria-label=&quot;the docker image and harness build making prod like the default permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Docker image and harness build: making prod-like the default&lt;/h2&gt;
&lt;p&gt;A harness is not only documentation and agents. It is also the infrastructure the tests run against. Three engineering decisions did the most to keep agent-generated tests honest.&lt;/p&gt;
&lt;h3 id=&quot;one-production-shaped-image-curated-overrides-only&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#one-production-shaped-image-curated-overrides-only&quot; aria-label=&quot;one production shaped image curated overrides only permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;One production-shaped image, curated overrides only&lt;/h3&gt;
&lt;p&gt;The application Docker image used by the E2E suites is the &lt;strong&gt;same image&lt;/strong&gt; that runs in staging and production. No &lt;code class=&quot;language-text&quot;&gt;@Profile(&quot;e2e&quot;)&lt;/code&gt; bean tree, no &lt;code class=&quot;language-text&quot;&gt;@TestConfiguration&lt;/code&gt; that swaps in fakes at boot. The only thing the E2E run does is apply a small, curated set of Spring profile overrides - logging, the WireMock stub URLs, and a couple of JVM options.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# application-e2e.yaml - the entire override surface&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;pattern.console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;%-5level %logger{36} - %msg%n&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;level.root&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; INFO
&lt;span class=&quot;token key atrule&quot;&gt;mcp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;external-server.base-url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://wiremock:8080&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;oauth.authorization-server-url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://wiremock:8080/oauth&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;spring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;datasource.url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;jdbc:postgresql://postgres-e2e:5432/mcp&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The discipline here matters more than the file&apos;s contents. If the production image cannot satisfy the test, the test is the place to argue, not the image.&lt;/p&gt;
&lt;h3 id=&quot;per-suite-docker-compose-and-the-suite_containers-fixture&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#per-suite-docker-compose-and-the-suite_containers-fixture&quot; aria-label=&quot;per suite docker compose and the suite_containers fixture permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Per-suite docker-compose and the &lt;code class=&quot;language-text&quot;&gt;suite_containers&lt;/code&gt; fixture&lt;/h3&gt;
&lt;p&gt;Each suite has its own &lt;code class=&quot;language-text&quot;&gt;docker-compose.yml&lt;/code&gt; next to its tests, declaring the WireMock instances it needs and an optional auth proxy when OAuth is in play. Suites share the application&apos;s Docker network so they can reach it, but each suite owns its stubs.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;wiremock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; wiremock/wiremock&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;3.5.0
    &lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;e2e&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ./wiremock&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/home/wiremock
    &lt;span class=&quot;token key atrule&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;port 8080 &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;verbose
&lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;e2e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;external&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; mcp&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;e2e&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A shared &lt;code class=&quot;language-text&quot;&gt;conftest.py&lt;/code&gt; exposes a session-scoped &lt;code class=&quot;language-text&quot;&gt;suite_containers&lt;/code&gt; fixture that handles the lifecycle. The skeleton below is illustrative - the project polls health endpoints with a small retry helper rather than the testcontainers built-in:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; testcontainers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;compose &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; DockerCompose  &lt;span class=&quot;token comment&quot;&gt;# custom health-check polling below&lt;/span&gt;

&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@pytest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fixture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;session&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;suite_containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;suite_dir&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; app_logs_dir&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    compose &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; DockerCompose&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;suite_dir&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; compose_file_name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;docker-compose.yml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    compose&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;start&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        wait_until&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;lambda&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; wiremock_admin_ready&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;compose&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; proxy_healthy&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;compose&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; compose
    &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        save_app_logs&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;compose&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; app_logs_dir&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        compose&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stop&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Sharing a single WireMock across suites was the obvious starting point and the wrong one - stubs leaked, recordings overlapped, debugging a flake meant guessing which suite poisoned the global state. Session-scoped UUID fixtures (&lt;code class=&quot;language-text&quot;&gt;test_run_id&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;workgroup_id&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;user_id&lt;/code&gt;) handle data isolation the same way, even when suites share an app instance.&lt;/p&gt;
&lt;h3 id=&quot;jvm-dns-cache-and-http-keep-alive&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jvm-dns-cache-and-http-keep-alive&quot; aria-label=&quot;jvm dns cache and http keep alive permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JVM DNS cache and HTTP keep-alive&lt;/h3&gt;
&lt;p&gt;This is the fix that earned the flat CI curve. Between suites, Docker restarts the WireMock containers and they get new IPs. The JVM&apos;s default DNS cache (indefinite TTL) and HTTP keep-alive (reused TCP connections) kept the application talking to stale container IPs - which meant restarting the app between every suite, and the wall-clock budget evaporated.&lt;/p&gt;
&lt;p&gt;Two files, mounted into the application container as &lt;a href=&quot;https://paketo.io/&quot;&gt;Paketo&lt;/a&gt; buildpack layer overrides - a Cloud Native Buildpack mechanism that binds small files into the running image without a rebuild. If your image is built differently, use a Docker bind mount or bake them in.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;properties&quot;&gt;&lt;pre class=&quot;language-properties&quot;&gt;&lt;code class=&quot;language-properties&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# e2e/java-security-e2e.properties&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;networkaddress.cache.ttl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;networkaddress.cache.negative.ttl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;# e2e/jvm-e2e-options.append
-Dhttp.keepAlive=false&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With these in place, the JVM resolves DNS through Docker&apos;s &lt;code class=&quot;language-text&quot;&gt;127.0.0.11&lt;/code&gt; resolver on every call and never reuses a TCP connection across suite boundaries. The app no longer needs to restart between suites. The &lt;code class=&quot;language-text&quot;&gt;e2e-test&lt;/code&gt; stage stays at three to four minutes - image build and schema setup dominate the budget today, and we watch the curve on every CI run.&lt;/p&gt;
&lt;p&gt;A caveat for modern Spring Boot stacks: &lt;code class=&quot;language-text&quot;&gt;-Dhttp.keepAlive=false&lt;/code&gt; only covers &lt;code class=&quot;language-text&quot;&gt;HttpURLConnection&lt;/code&gt;-based clients. If your service uses &lt;code class=&quot;language-text&quot;&gt;RestClient&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;WebClient&lt;/code&gt;, or Apache HTTP Client 5 for outbound calls, configure keep-alive timeouts on those clients explicitly - the system property does not reach them.&lt;/p&gt;
&lt;h2 id=&quot;specialized-agents-skills-and-commands-across-the-sdlc&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#specialized-agents-skills-and-commands-across-the-sdlc&quot; aria-label=&quot;specialized agents skills and commands across the sdlc permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Specialized agents, skills, and commands across the SDLC&lt;/h2&gt;
&lt;p&gt;Today the test pipeline runs four agents - Planner, Implementer, Reviewer, Runner - with a single human approval gate after the Planner step. Each one has a narrow job. The &lt;strong&gt;Planner&lt;/strong&gt; identifies coverage gaps and turns business intent into SCN drafts. The &lt;strong&gt;Implementer&lt;/strong&gt; generates the Python test, WireMock stubs, and &lt;code class=&quot;language-text&quot;&gt;docker-compose.yml&lt;/code&gt; updates from the SCN, using the &lt;code class=&quot;language-text&quot;&gt;e2e-scaffold&lt;/code&gt; skill for project conventions. The &lt;strong&gt;Reviewer&lt;/strong&gt; checks the diff against style rules, the API contract, and the existing test corpus. The &lt;strong&gt;Runner&lt;/strong&gt; executes the suite in an isolated container and reports the outcome.&lt;/p&gt;
&lt;div class=&quot;diagram-wide diagram-desktop diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/f7b59e2bfc193bf5e359790a5e04c73d/277c6/hitl-pipeline.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 9.846153846153847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAIAAADXZGvcAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAd0lEQVR42gXB3QqCMBQAYN//hYRCkOg2wnYXRZS21E0vPNvZ+ZGt76t+T8gaom3YX0SFSeTW7bPfFdC28dsU8bx2oa8zT7rdoa9LentGx1CNr5A1RtvScg2YEEmMUedVIU1nHE9ZFlpNGI6ZZ4EHDIeCH0foaPsDWlduijjPPAUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Horizontal flowchart of the human-in-the-loop pipeline showing an engineer writing an SCN, a Planner agent that proposes a test plan, a single human approval gate after the Planner, and then the Implementer, Reviewer, and Runner agents executing unattended before the work is merged&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/f7b59e2bfc193bf5e359790a5e04c73d/3c492/hitl-pipeline.png&quot;
        srcset=&quot;/static/f7b59e2bfc193bf5e359790a5e04c73d/ff46a/hitl-pipeline.png 325w,
/static/f7b59e2bfc193bf5e359790a5e04c73d/a6d36/hitl-pipeline.png 650w,
/static/f7b59e2bfc193bf5e359790a5e04c73d/3c492/hitl-pipeline.png 1300w,
/static/f7b59e2bfc193bf5e359790a5e04c73d/d544a/hitl-pipeline.png 1950w,
/static/f7b59e2bfc193bf5e359790a5e04c73d/277c6/hitl-pipeline.png 2522w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-wide diagram-desktop diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/46b8f949707622d0384201d9796bf672/277c6/hitl-pipeline-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 9.846153846153847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAYAAABYBvyLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAqUlEQVR42mNws44tcDSWVXY2U7joaCK/xMcnUiQ2NlvYQUVloZu6tqOLi6qmo5nCWSdzxSsO+oLWDiZy0xzNFN/aaIv4OJvKFjmYKr610xNJ0o32U9f2tLBmcLeMyfMwVlF2tlA67mgmP9vG1c/cwyNUy1lVdTrIQDc3HQ0HE/nDQENP2RmKWTqaK/Q7mSs9tDaQ8HS2kMt1NFd8ZGsgEW8cH6Sh7WVrBQD3ySvfv15WJAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Horizontal flowchart of the human-in-the-loop pipeline showing an engineer writing an SCN, a Planner agent that proposes a test plan, a single human approval gate after the Planner, and then the Implementer, Reviewer, and Runner agents executing unattended before the work is merged&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/46b8f949707622d0384201d9796bf672/3c492/hitl-pipeline-dark.png&quot;
        srcset=&quot;/static/46b8f949707622d0384201d9796bf672/ff46a/hitl-pipeline-dark.png 325w,
/static/46b8f949707622d0384201d9796bf672/a6d36/hitl-pipeline-dark.png 650w,
/static/46b8f949707622d0384201d9796bf672/3c492/hitl-pipeline-dark.png 1300w,
/static/46b8f949707622d0384201d9796bf672/d544a/hitl-pipeline-dark.png 1950w,
/static/46b8f949707622d0384201d9796bf672/277c6/hitl-pipeline-dark.png 2522w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-mobile diagram-mobile-sm diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 322px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/0a27ed851f59fbee5ead5c61f4b05c31/fc778/hitl-pipeline-mobile.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 577.639751552795%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAB0CAIAAAAKH4UAAAAACXBIWXMAAAsTAAALEwEAmpwYAAAMP0lEQVR42q1Y91MjRxbmz71frupCnavOd+eczvbaXttre+1bNnnJYQHBIkACJRRGQiggECiAchxp8oymu+dez0haggS7Vzf1aup1+Ob16+l+/fWbMK4+hBCJ63XqylVRZUGHpmudJy4XdA1nI53sPlcIixeMUIyIVAkLoJ962PNYF+lkLDjDsAAIbeYCG9mku+Jfz8ScxYSrfOgsZkLNnJ/Lx7rm8C6DzYLY7aV2W/w5Wvot9Py++5O/zP/wwcbjr3cffLTx8LOt4Eauk+2l9pqarA8hE6afVGMrSma/00ornUyPPdMywVbzRGHPepWEWEkIjWOlcSxDB66pDiGvLQusdubtnB+wK5Nh19LxyiSz8DDgt2VsTw9ePmLmf/HnIyx4rohXLffn2SCZUCezz+7MJ+N7ZffyiWMuGXUUwlvn8InIVj7tbuWjN30ePKqknwXYAiNW43I9qTSO1GpcopKQYbay4U5Pw2PB8GBEOg2lmhVqObGQ7mSSzXpeqmR4rqVifOt/Hs6E9Siq1uVFa+VcaxoNtjpZNiRZ5QWJDgffXF1jwMNHlBRBlEfavA1MBmCQNwATTDRuKEiFX6KEw8xRMgYKFC+18tB5ADY13PCj8+eovIRKCyB6cZ6UlzrHT7nT56SyqBcXrHpUXtbzT3A7ZtkbgCvbmHMS5YDIISKHDT1mKBGiRY1e1ICiGiEyQ6QQUQ9xewPV96+Cqw7c3iJC0BD8ettVS8wodYfe8WjNXQTv1i7uegm/T8QQbqzhRuCaZQdmt4gYNBSGu1j3Pnln9+Gf/C/+EZx5b+/XP4fmPui1XYYYIBKDm7Zx4BAR/FjwI4nhS5tCbUduuYSSXW27iBSklqXwbWBDCopley052z572TpdbqWXQKmn5uXajiEFiDjeMgaftUje/yhu+/J453tm/sPEq3vx9a8Olj+FT4BHWLxr2D3WQ7RDooT1rg/mH8uM0TuE6bhj2NSywlRiUzHbl8lX98qHL9LOH5Ob9xIbX3eyq4YSusWynYgBcAxmu3TwDD7RTC/mA/8pH/5eYB7L1W1D8tNf1YRf5b8G3sFdJ5EP6MjVAwMlQIgKiyRGdT1B5AhtUg5x6xWuXwWj8ja6mMTVRVyZQ+VZEL00AzpIrzijl2YtHdeWUP4hfr3CrH3REwiXI3wec3lDPO9Wj5TWKRQNtRQLOaLBbUMtI7MD4XNEly3Q6EjSZAUdoX480XROVG/fz2QgRq/Xa3c4K6KZAiFFxlg3W60aMjYYqJrGdnlQNNjj2FCRIahII6RHDEvILZEE4l6HE5COQsxRJJ47raF4uhYIxENMKhCIhSPHENssB0dZNoOmrqPt9S3HK2eyIAcj6W2b3bG5a1/d2FxcFnjBCqkjwABrsxyMjdcIp+KuQlqizmkG3yPw5lQDkbFxm7Y02134hFWDkK4o6qW4SG4J+rSty8EcYVSv65VKD2Nw5Mr/uB1cYfm9Dac2NydNTwsXBTwm/I4GC4Vi4aeHBhPCwaAwPd2rVo1R+Imb60uv16XZGRIKEoYh4TD2+6WZGX0UfuI6slYTp6YAADASDFIx8eL0NPh/DT9xE0n8fuPgwDJrwCdCISMahfFD0zX8xGtkowHDAyRmmJ7Xq/t8mscD0oNhOxzS3p4RDNLx12pDyGuwuLjY29w0UqnK7Gzgww8jX3wRu3cv8d13xw8egMKurhrJZM9uFxcWRoCxKEqLi8Tt1vz+9toaa7MpLheI6vXKu7sIPHe7oQPm+avgQQFxnDQ/jz0eIx6nboPP4Lnls8cjLSygTueGz5fx3a44P49cLhKJ9Gc7EqE2AcmyY2b7Bh4ABuBhtGATPnfV5pj9PMTPzWGvF/t80tzcSOQYWnEJL83O3hztXZzEpENCtSZWqsMF/3aERhgSGuNt2FCfSomyJCtvR6WsnrKGzptyripp+jjDoxmgGQ8KxZWnT+1LK2yzNc7tsWCOFxOp3Em2KivauJHf6rOs9H1+qwmzjFcb3Uabv/VX6RLpHhMufVlQ98RQcof+9ZPoNii4e3KtAxWkTpDOEcr8gGtwMs+g8oxenNZLphSncAWO5VlQ+jWlaVymNbi6gDIPMJ+bALNA6ogaB7JClKjJA+JGLw5sgCpDMespv4RuagzVV01w5xgIARFClI2c23K+X6vxqVL0+XngETATUCqxF6BAERRgk8AvgWWiysLAcm0J2BmQHSBb0Zef+Z79HRjY/vN3D1e/CM68z8x9cLT5jefJO0DOEDBQAEshBATkNVhiTJpFSRzifVhhDJIE+mXoUVCIdkCgRg1TJO3G3AAL+9AG3wYq1kjNH9m/BeKX3Pg6svgxm1kBWok43x1gSsUURqo7wIVObhW4Z/NkUao5YES0lcpIML8PpFhr7Um1Hb5kB0WsbCMhoLMeqbIN3+qxbvj0aMuY8xm9SMbzy/7v78bW/h2Y/ldw9v1meilu+wrmzz35N6DC4DZ0uwZeHloGgi+UNoHjA8EH4gp+oo6XSteLr/i8hPk8XWGwvEx2SB2GOwb4TIBaAzs3Bf6/Qd9B2goCeDlMLXPZCSIW9dxzVFpGxUUQvbAAgkCKi2p+DgRDvVVTWLT6oOKSnn9B5IZ13OBrQighJYJIH7qpML7Rh7zBje5tY9jr6CnKgnTnXZKgEQLHpiQB6wSFjOiA74gk5+Vmqda+I+jjdhRXd3HNg2tuEARSdRnt/ZTv9wwzCwoUrSZTPLjiJJ2kCUYQoKdwax237bi9aQlqvULNDdLdwh07aqyDDjWDVjturaGLOYNyT6Ti8oq5ABh6i+jfMSB0xOCCRBWcpGFEPYAYYHagWwiV1+A+b4JLy0SgSw+WJ6x+/9Q/U1vfwiJPbX93vHP/zPUzFCGMGNaNUIC94UGl1RFgWNXQ+8j+zXlw8nTvpxPnjyc730NIgashvdGNBwesHWvoh3CL0yHi9KJIDiEpZN6izVBDO4wH464P7J8HH4cXPoItCfEEvCgwTww51N9Vt1mGWNU7kBu7Ut0plO1SZYsvbkLRQGA8dIfP3fP1Tm6NO19vZ1bY7ArgleYuVDaPFyS4EdItOd4yBD3Hz3+E0AGhz/P4nUL4KdyFXY/+erD0CYRuOnjefxVM/7Pf/IdBuPlKNcg8hA3INmhRULAYRLyfpiBgzsAAvSb7UNlGwXBeocIs6e6Q7h7p7uLOLuFduOPsS3eXVoKwTnpTpcU90tmGwNC/S6K6B5XXUWULVexUypvwxtUt5WIdBFf7NQPZQqV13AzeyYbkN0nvkGtiXflEAMvyYEtelTvDkGn57khCbho3OcmAhxlvZdlsS50Vshd30UciFNA5RGMbKq5Zol+sGNWNC2ayEn0Gil5YHTZRuVgmcm0QhhohXF8m4j78YcK7QQzRizt7RPQRU8Gc26qnHcR9SmDa8QG4FcEQdKSwtWlgAWotF2xDeqarEQgpkFcjvL+/cyDPUrdhNjkAN2nepX/WSUGl7rTf/0N8/cvoy88T618Bs0jvPuifUuYph4HNsIkbYIsccL5acg62NORZGscLuf3funmbuSX2rZP9NrDOuntdb4/zaawb4gnocNDrl0720WB6vqvhi9Dj8OLHEEkgHwVZISBBoNeP5gyVsU72MZbBZwhagl9ruyD1BpaVhhN0ueagbMaKJKPBkEmTQ630YiU+3UovVxMzZ+5fIKpAAOlebACPg/BCPYfc0g3wGk0AysH22TLksLLehyeO72HCgRBCYq6TfQkxkC9sGDIEgyCur1wCw39u2yHjRE8DoJ9azHpjJQoJMIMm1qKUd9CcFUMTU81Xl/5zI4AKj3ELsmQrqP5yKFC0arCp92taNpq/asUGa1tpQpYKNyHLFhoKaTFyySuXvaBcrqfSCBCt+39JFNPIcYOyGASYwf9CaAb5bZkXpbe7DuLBMGVV46zMOqHs6k2TLNb75Owilc5bRTLK/gjLEa4034jb2OPJs32QtXZqoZFICrWbF6yJy6OFd4KvTTWjh2op2isfG40T0kyS+oFWfFaPZKXrzOgS2Kxdb534pOzXjrn3bE/ue5Y/3Zr6fGf6gXfFKZw62xl6vAxy8iPAe2xug0t5aqmtUmyvlpwMb24Wor7m8ctOMtQtDrtdB/cXhq6Bh6+EtFPObotnDikLio0/WWkcaRgZt+fDaOoS46zYPhWaZ2LrTGilhWZeZC+Pdgj+L+yRBq0dslr3AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Vertical flowchart of the human-in-the-loop pipeline showing an engineer writing an SCN, a Planner agent that proposes a test plan, a single human approval gate after the Planner, and then the Implementer, Reviewer, and Runner agents executing unattended before the work is merged&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/0a27ed851f59fbee5ead5c61f4b05c31/fc778/hitl-pipeline-mobile.png&quot;
        srcset=&quot;/static/0a27ed851f59fbee5ead5c61f4b05c31/fc778/hitl-pipeline-mobile.png 322w&quot;
        sizes=&quot;(max-width: 322px) 100vw, 322px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-mobile diagram-mobile-sm diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 322px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/49591c41ee51d908321c64ec715aa33e/fc778/hitl-pipeline-mobile-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 577.639751552795%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAB0CAYAAACFfRJXAAAACXBIWXMAAAsTAAALEwEAmpwYAAAQsklEQVR42q1aCVSUV5YubRNjQJbaq6h9oRZqLyigWApKKJBNWQqEqAjILqigEJdYJgaXoCLjgriM0Szm0Imd5ESHTDRmptOTdM+Z0+kzPWe60+d05vT0mZnM6aRn+sQYKaru3PeKwlIRSp3/nMt7df/3vv+++9677977YDAefBaRP3+8Aqzjg58YT/XfNITpGNKZgZ+mwEcQH9n2IQ/QlwDwVE/10X0uU+2f7KqiO3Z10W2rsvAHu9rzgw3rqeriOwWp6/6jr/5kH2OBZzH50+MdOWCS58OKjMrpQmcNZBiLwJPlhWxrCWSaiiHHVgYmVXbAoVkJ2xvGtoa6+hbfK9uMdJ+dBV6Ro/FP3DglbKzdFujd4IORvRdh79ZR6Gt5CV7oPQo7Nw1DYWbdtDBRCxXOtt/hiJ55YPg+X+gLo32fWDP0ZQEp1wgiVkpQwjaCTZMPCp4FJGwDJIvSKCkE1qCcZ4ZsQ9Wfb47clkViMGb0RtGvj91KyjPX/m+KIhM2VG0NrlrRCE01fdC6ZgBW5tTDutU90FS9FToadgQMSid4Utf/G3wNsZFz8IAON5a9fCpNWwSVnqbpLEsJlOevheqiZnClVoDbUQlF2XVYL592Gsqht+qoL7LvnHqELyCmpWzfebtqpV8ncUJyUgaoBQ7QiZ2EgqR06lf/0L764GEc2RJGFA8FnjzytWp0yw3PqS1/5z71/M2yv9o1WTe2+ad5YwOfFN448a00inV4FxDu08eJiYnYExcm+JH6nlkqUQHOTpTXO/EjUj9ydpJ57Ox7PFL3ToR4j/ssCgFOMA+Pv8F+lGHOC7j/+BXW/uMXWU8MiMOmy2Hy6vvV169fLYzkPfSZ8DKoTi72qsqaPMLP6t3CGw1u4U1CawtENxvy+R9tKNP9y4ZS7a9IPfyO0Jo84cetJaJPfzygdNKP+XA9+lwMupZG2pWtVg0HlGJmIFnKArWECVLBclBKEkGeFAdyYTyoka8UJ9J3GikrqBAxAxkpXHh1s7JiVrgw4OEW+UaHngM6OcuvVzADVh0/UFFgDjiMooA5mRew6wUBUzKXlkY1J5CiZE1rZEx/tokLZ3vk5Q8AvtIsa03TcUArZwXUknjIS1fBwZ3r4Ni+jfDitjrw9dXCEd8G2L25GqxaPn6YGUyWsQJZxgUA8asBvYIFhJIlCZBlk4HDJAabXgBOqwwsCKaRJgBKGEyWMhcGJBJqZYmQaZVCRYEFPDk6KMrRQ3GuHjzZOihzmyAdP4BtopNQp2AHlKI4aFiVAf1tZdDd6IE9W72wuWUl/b1jUyUUZmtxYuKDmuiGzKJDNmt4IBPEglocDzgJoBAuJyAg5ccQ6aIfsk7ODhCQqiIb9KFERLKqYjt0rC2ArRtLYAuSOzOZ6Hd+CY8goIMCog6lieBKU0JtaSoCW2GlKwXWVTmhZqUd6ivSIcMsBh3qkABmzwdo1eLCljADGjkLFKIEEPNiKSmSEkCGQxbzYiiRxY1tggrcBOkGLox3Ryzs8NY73a1oqnDyocDO/c6Tyv3ek8b9vtjB/67YIbhNfyOtTBd8X5zOvzXz/pbbzvmuKkcQeK1fXRq5jenz6xP62K8umGW/P2eV/uuYWfbvZ1Si62Mtjs/Pe/WEBxcZrNfPDHW/dnr/FjjLYP7mTI6c8L/Ctn+YyFgWldnad+4Dqc838XSY2T/8Rcz+iz9nRWmq0PTPEPk9Pj7+7MjYZdn97d799NPlC5qwuR7fiYnY0dfeERFplzFN6cu4tgymOsMhNXvyn0k0ZC5h6h2EvyRO72AI7M8uCHjo3Lnl73z4BTc93c0rKKuaziqsAInJFbTlFAeLV9XAirKqYPFqL7hLK++4SlZZHvAeHgA8dG75qUsfct3ucl5TW9v0upZW0DiKgvmlXmhqb4d1LRuDG7s6oXdrz50165qtCwL6fBeeOXn5J2IGwxTDUjq6eWpnD0+btUlkyt/GVGZsihOldmPZw1Gld8Ym2xc6wEJn7+j4ZeXo6LWlEWfMj5CWPvZpd+j4m0LvxD8s++/KSsEfV9WJe659uXRw8GA89WVcriWztNBJGPYOfBOTqonx1/u7dLqftRuNv4ThE9KQH/SIR2lYubcPHJBtc+X/IUUgAGNSEnSbzb+A/n4ubTOXxzUn2ExD6Otj95hMn6bw+ZAiFE7rBQK/OQT6WdSgkWCbLZbPTCIR6IXCgFEoBEI6Aoq8TUTS3l7evKBhvcyCoTRaHm+aDFePUhIyYF3D4/lJSUFbWnhz6jTM+Nrr5feazT8jkqUkJU3bZTKwSaWQplBAqlwOFokEcrRayNRoKCgO//Pfer1JD4B6GSE7dj4/fygXO0uZzDt1Lhcc6O6Gva2tsKuxEQbXroUttbW0XmK3g4zFupODHzmdnT38gFs8O9wjR5Ztt1qv2FFCq1w+vRI7EkpXqSgRKZ3JyVT6VLEY+iyW93GCYuYdNgEdsNnesyGoks2e1nI49+gwGfVqR7B+q/UqDA/HzLsuZ2d5BjQNdabDJUNnGScJZ3k6FXkU7NKlmAUNwj2gFy48g6DvkqERIAqG9W1W6weAxjcqsLlAB+12OnxC22y2q48MFqHTEOi1a0v7rNaPcG1+/Nhgs6AzHX+377D497v2SZ8ILPI5fvEKi0QCjP+HZ9EcgI8bBYSGJjPm5MrMef+oTSv4e4k+W//QYHGhx+v10u24pb+nZufubdDV2zXd19fjfGw9hjt5Kqry81au/nWGu/wXmYWlhpmXi58wkrrI8h15Yh3ePVuOXLqpOvH6Dem90ehDng+HTTGvbpI7xtofpFMd2oyzdQzdsaG+/aMvdw+OPcfQn+pQZdzf7kSbLP3VXqX9y1HVUsbf+LS2NflCcNt4UJjKC3rS+IA+IRSFKV0IBTbONPqNgSKH8C4fCdvTPqTvcyuEf/lPdAcZ7+xU2rKNHAy/EgGd8CDxVsW85RiWxVES82Nn6xJBiC/hh0qVmIm+NjMoFzEhz8L9C/EVEVBry7dxSXyC3n1C0OVQQmNNFqwqNFOfeu3qTFpWo+NO/G1vSSoNOSo9FhoMYRgXJH1R0ruAeVYuCSeoZ1+QpYXtnavg5YF6Goa9PNgA/e1lGKvUwAtbaqC3qRgO7FgLW1tLabhBAEnfAvt9gDhcGntoZUxQJi0Hg4pN4xIheylGorEgF8SAkPk0OvDL8cPxoMLgiMQzKQoWiageAogvSSOjmkvBSvON0NNUBINdq2i8sru3GgpxBCTwCUkXJSBGmhQQQ1sahuVlqKEIY72SPAON88gISJuZIHMeQBwyaYgxMe1IIlG7XohRqBT0yMe4GTItEhpEWkIhbihEexigUcUJEh0117lg/47naGj2EsbKL/bXYTRloMEjCSSH9zTSSJXoEXV9L+CPd+us+RbuFAL6DUrWHfzqVIZJNJVtl02Z1NwpjOKnUF+UTJRCPASawuFOGRSsOyREw2XzzVdjGhndKXlWHuYTqE6COhJ4S0leIQHXJhMngEWC8lCdrlUmJYwJQTejQ7UstA7/THbKh/uS5QM1kjdbVya92VF6lzrLxLRsLxG+0VWheLuzQvY2qUe2oe1Kk95oLUl6a0et5PyXoxbOgsEQKa9cucKanJxkRvKisXtzETWkvsPj7IjM0uJ52kdnYIfOT3AeKffl87mWtLXZn5qLiBcxNDqKgOMEcLHXq396rnb3hLYLW+wb5pGLf6uLymKT5/V+de6xNkXjSLuiHssGSh2kVK0d3ZCw6uiuhvNHd9SMjjTGrya82TZIIxtpn8aJAbVjFrC7QvRuJuYOHCmc6fQUDhAiOa1MIw+cSKmaxGCajhl0mviUl4FpgXA7TG/5szBdNeCVjM8CrisQ/kQrZxOb6Eci9i2oFMUHJfxYSnJhXFCKpZj7bFDMexa3J83ZUMLEG+4YNnSUik7PAjYWCN9NUbLJqp8mG96i4eMeXkOTQBtqc6FrfSFNs5A9TngkBUNMWMgysfxGtJ1dZaLxhwLiXqWdN23wUHNP6m0NbgRdQa03MWUkt0P6LAgYTqTJ0EKHs0pyzDCRBBohYr1Jkg3NVnQSkkYGFSaE0AAQ6Xb2VIGvv5Za7n3b18CaigwKiKZuYUDdDCCRjOS6UlOSINUgomk+YmSz7XLKI9ITaaPWYV66mua3SFKSHAEFTg04LTIK5nKo6FGQaZFSi41CzA1oiJDw+e7VcGp/Oz06ySQcfqER6soc0LmugFrrnT2VsLbSGZbyQcD1hcJ3MG9IxP8BjagfjavfYRTTEo8EpDhaRyOL+Vm2H9ck/k70k7ZIt8nocB2OzQK2lYjes+m4mDPkTFswU2zDpJpJzcKSC4RPfpMMMiH6Hnkk8Ubq5mSO367nwtbqiJ1yvEOxZnu15MD2Ksme7dXivYQGvVJabq8S7Rms1xwarNMcovUwP0zYBy3+wdOdoZRf1E772bOT0TucXrRlLswj3k92O+MpArBjaIjnGx4maYFFhDdXW2809jAszeHx99lEyqglJAfPXF91hbKfi8iZ4jt8mD2fhFEeDSFv/9Tl6zlnJz62Rx1SXNulUr7YID04WC3Zv8N7l3avURx4vorr87Xnf7BnY/bbpL6zVnYg/H5nrWRo0Cs5MLRe+hJmSPmzgO/tSna7LDxqjkzqu2RQMnG9IV+REDQoEoOkjususk1Qj33cdu4PvzlpMM0CXh7U5GUaOMT6+slmD91YsP1WncAvF9KdQrLv05gU96vE8bNtkKbwSiSQY+Z896vjRuMs4FvbNflOI82yUzunQd8mDa3M6Ve60NEsgW0d5TQp7uvzQmt93qxvGHY2EfD2Px2LkPAewBmHk9i78hUmtH/psLrQAiX5BkyOZ0EeWpuQgWXPOptRAZK7AHIUEGNrmamjK4eev5CeOdTZjAaQOOvEkaxHy7yrt4pSH3r8xOsnpmw3RgLkCgR1iZPGXgiQSYdChkR0ZNUKqOtLKNWQBDhJ6HeL6TlD9Bx2hx8KSBzKZDSa5EKm0mOllzKrUH9NeJS60XEnfncuXjp4S9NoiQaWxihzAmaZONQj1cgSqNnvbS6mQJ14dJIZbqzJhiw8V8hxsKbcATmpcnrrQ4TItdwHeHlQmefQs4N4C+EnN2FKUYJfJojD9Rfvlwnj6G8cmh+v42hdhdZaJU4gbacU4sRAloF97zq86kt2lzsF1I/JMRPig8tCiEdLwiPXbblYz0UeoVA7XpD0WZ0luHenXB8y8A5uUDTseU5av/d+ahDX7Ws2txLa2yCsu/+9r0HaMNwkq/3liDnhEW5vJ5mPlGYh9pDc/NxPbTMWexit9RBabVInvLnaPpKPTaQbRT/7yZIYJG8zI6FvZCRh59FxAWHb7fanwjmdR0jahEKHsHU+efGa68ylq+4Ii72IAdFLShvqDjWvUDYWvinxZr9la676ub2l6nNpbe5bquaii6aRtvTohj8jkeVkZ5rEk3aLr5cDVycLxsv5wTg5L5ioSgoKUjCLXOH8L+vE86oFs0wuH715YKh6KvaIbBpoHOi81fFi/5R30wZ/045N/vWDXVPervW3khw60A54WyP7PHwiyHB3NawTZxvBkGUPWN2ZYMhJhfImL9gK8D8x0i1BUa4ZUl5p9kT2mVd/bfhPPMr2kjGuU/8/nDTNN9wM3bfstORvSJ2XY/w2uadi6JGTaD68avP881+LXTePqPJvvKIkROru376axAhFU3NOyP8BnRvy1RCFpUAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Vertical flowchart of the human-in-the-loop pipeline showing an engineer writing an SCN, a Planner agent that proposes a test plan, a single human approval gate after the Planner, and then the Implementer, Reviewer, and Runner agents executing unattended before the work is merged&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/49591c41ee51d908321c64ec715aa33e/fc778/hitl-pipeline-mobile-dark.png&quot;
        srcset=&quot;/static/49591c41ee51d908321c64ec715aa33e/fc778/hitl-pipeline-mobile-dark.png 322w&quot;
        sizes=&quot;(max-width: 322px) 100vw, 322px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;In the matrix&apos;s vocabulary this is human-in-the-loop, Stage 3 on the Human-Agent Interaction dimension. It is where most teams running agent pipelines on production code sit, and for a reason: we do not yet have a metric that lets us safely skip the plan review. Across fifteen scenarios, that is fifteen approval gates total - one per scenario, between the Planner&apos;s test plan and the Implementer that turns it into code. Each gate is five to fifteen minutes of human attention in our experience; the team is still calibrating what &quot;good&quot; looks like for the plan the Planner proposes, and that is where the time goes. That is the bottleneck the data already shows; the commits-per-step above 0.10 are usually not the agent doing twenty revision rounds - they are the agent doing the work in one pass and the human approving and adjusting the plan in two or three.&lt;/p&gt;
&lt;p&gt;The direction we are building toward is wider than the test pipeline. The SDLC harness loop in the previous diagram already shows where we are headed: domain analysis, scenario authoring, contract drafting, generation, verification, and sharpen - each station served by a specialized agent or skill, with the human moving from per-step approver to system editor. The shorthand for that state is human-on-the-loop.&lt;/p&gt;
&lt;div class=&quot;diagram-wide diagram-desktop diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/d08b4d462e48ca7a71efee5e5430f69a/69f8f/hotl-pipeline.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 20.615384615384617%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAyElEQVR42j2OUUvDQBCE8/9/i+8ipU8BRZBaocG0D5aUJLWa9Hrp3eV2b3fPrYIf8zDDLswUWbJCLBGJ3c42CxieElhkOYyprM7bI2ZmrGsZBxShRBfwle2mNBdNZd4fj1cLZz+jeR13d6F/CP7beNh/4WJ1qjoQgLks4WPvk2AiR7GNF89YdLXdPp9mh9rP6CVNgpMwaXRRhols+NtGxGxcNCG8+Xb5udmEvsi/iO7RIwtLVqlViXC+Jf5HP5Hp5drc9+u1a38AR9XimV1QzgIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Horizontal flowchart of the target human-on-the-loop pipeline where an engineer maintains the spec, an agent pipeline produces tests, metrics and signals are monitored, and humans only intervene to sharpen the spec when signals indicate drift&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/d08b4d462e48ca7a71efee5e5430f69a/3c492/hotl-pipeline.png&quot;
        srcset=&quot;/static/d08b4d462e48ca7a71efee5e5430f69a/ff46a/hotl-pipeline.png 325w,
/static/d08b4d462e48ca7a71efee5e5430f69a/a6d36/hotl-pipeline.png 650w,
/static/d08b4d462e48ca7a71efee5e5430f69a/3c492/hotl-pipeline.png 1300w,
/static/d08b4d462e48ca7a71efee5e5430f69a/d544a/hotl-pipeline.png 1950w,
/static/d08b4d462e48ca7a71efee5e5430f69a/69f8f/hotl-pipeline.png 2474w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-wide diagram-desktop diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/2d0564a1375a882723c9725eb694ab7b/69f8f/hotl-pipeline-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 20.615384615384617%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAABKklEQVR42mOor69nYmBgYDT28eGKymwXtNARb3a3Vb9gaySzx8pQ2fL///9MKua+3YZOYf9VLfw2A9Uy2CsrT3BWVnaYeeYMa1paGpdpuLOanpdNnW6QsxKDg1HgYieToCvmxr6ungnZuuZ6Uus87bX+2xjJfbY004r0iS/SUDX3mWvtm/Rf3dr/WK+FBaejuvp1N23tAu3wTFkbm1BRVW8LaUUHk2BpDzMZBht937n2xgGXbAyD9UC2R7trSHqbi6gG2cgphVrIcILElOxCdTWsg4OUbMKsQfxcFRX20NBQTkuvMDu7qChdWRPN/doOZm+ljdV3MsAAyOtAzMbw/z8jQmw/C0jsP0KMEehF1lWrVjGnzUxjBQmIhtrzyFvonNBxMv8vZ6q1HwAxTFaopHRNqQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Horizontal flowchart of the target human-on-the-loop pipeline where an engineer maintains the spec, an agent pipeline produces tests, metrics and signals are monitored, and humans only intervene to sharpen the spec when signals indicate drift&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/2d0564a1375a882723c9725eb694ab7b/3c492/hotl-pipeline-dark.png&quot;
        srcset=&quot;/static/2d0564a1375a882723c9725eb694ab7b/ff46a/hotl-pipeline-dark.png 325w,
/static/2d0564a1375a882723c9725eb694ab7b/a6d36/hotl-pipeline-dark.png 650w,
/static/2d0564a1375a882723c9725eb694ab7b/3c492/hotl-pipeline-dark.png 1300w,
/static/2d0564a1375a882723c9725eb694ab7b/d544a/hotl-pipeline-dark.png 1950w,
/static/2d0564a1375a882723c9725eb694ab7b/69f8f/hotl-pipeline-dark.png 2474w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-mobile diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/c7dd03fac6105a3f46ebf96fcefb2c39/c5bb3/hotl-pipeline-mobile.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 253.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAzCAIAAAAYQr/UAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFZ0lEQVR42p2WCXPaOBTH+f4fYWd2drvbbLqdtglpgIQ0B4QQCDGYy8Q2RwEfGINtfMiXjhUkJWlCQmc9QvN4fj/+T9JDUoI8ffCyA4tY4Z2J4GqiR/u5BBDEZNOTeARXAY4Rqk3QOJVT79ij941isquw7qTrbYFXwlgTQSevXae6xa9iKd2rngyZk+GoagM7XgtshuMIKZwr1e3b7DC/x3cK2k2mL1zrSsM1J8Gb8OqFKriTJmjllXK63ylM2LOx3LAl1g08uI55FQ4BHFSt4kGvfiqV04PSYb9xpvQ7OkRwy5jvnwBEpuIbY9+QAtro5IdR5Hp+HMNXYYjwAkQ2iGY2cKP4WRzG2AN+9LM/sfIvMzZckKmKR6yQqYnFzoAghO8bfhgpDXOBj59M2qMyjaK1YCwACCHEZGNZhGFE2yOM3T6el7BVw6BJQNNSCrHNUgPbLDYZPCsR9BiNEAJ+8Agjo4LNY2ie69yO1fvsjpKzzu60veOND4hbQJN9HHuvwxaL7TxyysPyn8PKu3F1p1v4TanvLoYZEjBIP8IxeAOuYescOSUSMkb/q9b+qPNfoFsioIydEtIzW2BknpGwOu8dSLX3k/ZHmd2ZtP6FdpGAGzR9CYc/pU3HpgtJmuqY+ZuK6/yezP6jcZ/ixTWeZd+EjQoxjoGScUYH9jDpq5lwml3a3/eRlds0YU9gDGRs1onTIR5PAjGyWtjhCODpV7xoY7NJUPwqvLbcEM8B0u3QAMjyN28eL2FMi5Ba7EDer3BXvJKpCqnqne359yX5NvxY+rQ3Fw79D+EfBb9VmfR1xCnoToH9GWG/e3cqFCb4Tl06LQ8/3QKewxCRtow5FXdn5LI9O2PVK85oK7EwJU0ZyyZ+uvtsgHkVixrm5PAbI+3n+WxlVBFMYQLvVKxa22AaJGg439BOq9I5q+Yak9R1v9q3eY0oW5U7KhJ1Imjw+GZ42dQu6kpFtAYGoWP+FWVElQUNUc3jyuiw2K+I5mD+yzBVbklBpjw4qymHV71CW+/NCPcrY6YKgk6a4+C6M7/tLoqdGdOzhSlqq3jLmOlnoOOOgsUJ7umErrY4xd3pcgmo0/LIW+v8rIYA8CHd/zafitvK03LcGEJC/hc8Z2qhppGXh9pb8AoNOQ6en0e5HDSMjfyrynGngy8vSb1OGAZeXsL5/CW/GY45zs9m56mUlU6b6bSRSsUXFy/5zTC8u6PwNJk0Uykzk7GzWZo8epE8hf3NafM8LhQIy9K0w3w+fiVtf+OELRe51YpOTuJcztd1HyFCNk5YsOmUpC+iOBIENJstf8gP/TDGy9PzWXlugqMYBsHDgSgosytxetEeH9e7qZpY6clvwUEMj1nRWu2YS9kwtrxo4Ueq6Uwszw5WWyLBECHP9+8vXbQl1ie7MrfDeENhojBe5w1j+NPhvrZ8P0DLSXr4G9F4OViIway9UJnpcAStQWjyjtYP5r1gLgBdCqwEfoTDexj/yKXsDgt+v+D2Ulr9m8GdGNyZxWfn7SIYUH/R+Z6IMfJQHBA4nOlOHAIMo9WVi16O6kBuQaXmj3bbuT+q2Q9c7hN/tdM8b4RSk/o9OdF01Q+jmwOVTSrsnsR8lm4ZR7pXZj25Fkn1UG7F6rFcT49qh0OmBSfUQ/2MJyUq7nh3UPzQLe5Jt7+3Tv/ic0V7QMk13IYaBfZ6pYNB5XP3mg2kDp4+wB6MxpEtxfY4WkjQobYNg/u0KVyPpPMplx5X93rl5OBmv1fOjNnTSYuNpBqFX64N/nEhvHXHTDTOKOyXQflIrR+Mbqn9dcxQuxIMK+4osV7xp+2hzjz91pWagdYOp7RvrRq1aU/9d970P3DCRA1VegxOAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Vertical flowchart of the target human-on-the-loop pipeline where an engineer maintains the spec, an agent pipeline produces tests, metrics and signals are monitored, and humans only intervene to sharpen the spec when signals indicate drift&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/c7dd03fac6105a3f46ebf96fcefb2c39/c5bb3/hotl-pipeline-mobile.png&quot;
        srcset=&quot;/static/c7dd03fac6105a3f46ebf96fcefb2c39/ff46a/hotl-pipeline-mobile.png 325w,
/static/c7dd03fac6105a3f46ebf96fcefb2c39/a6d36/hotl-pipeline-mobile.png 650w,
/static/c7dd03fac6105a3f46ebf96fcefb2c39/c5bb3/hotl-pipeline-mobile.png 680w&quot;
        sizes=&quot;(max-width: 680px) 100vw, 680px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-mobile diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/c80f2debf476d2808829c21dd4a9e085/c5bb3/hotl-pipeline-mobile-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 253.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAzCAYAAACXICiDAAAACXBIWXMAAAsTAAALEwEAmpwYAAAIGUlEQVR42pWXWWwbxxmAKV+J61pRKJEUL4mnSC1vLo/lsVweyyUpiZeoFSWRNCXZoWOgQpo6TWs5xQJ1WleQnVSN6/pC3Uj2i4AUeSjcR7+kL4VQoED7XLQPfUmdGgUaW1qSf3epSKVtnQPMzu7OP9/+M/+xMwLBKwU6+Ot7xSXtsP/slZB19C5uzt8hrIVbU+T7owBwhOvuEBykMAKGFxZ8MLtisKmj/8AdWShl5hoTQxdgCC81EGUAhrBZ5hvxIwdhHuUvQdPoLcychFRw4nkKn6wnAsV6xJ1nw+4sOLSx/zDV+6p2BXYr29PAkPTvjEoPRL2j9XSkAl5zAig/Dagx2jQoscbb2QW8BWSYIwfSMOGZvqqXucFvTW1woIbXTDVCaKY+oHRzGpL/XJp7JGpf771WsfXFi+VlMeEY+2IkVAEO1KT8xWYyOAkR19jzqfT7l16e0X6lJfjFvS9PT0S+l6HQMxdItPJ2yn323GxmwfAXgBPMtds9zP37r+8LQUn6DUs0i5uDOcKbHB9ypGndTrKc63RcvfnwzR2hAIIOmkZOtKybowlitPQ8PFp+GsqVgBqfflRD0eM0hp1sVXr1KA/bMsjNhxx0L8MQBHNMoLh+8tzlZXWs8km3QLf02l7TWlxePrW4uHzqhZdzOQWRx6XX0z7JhxHr6Q+jltcvj8UH71Bo90+j5m8xXPuTtF92tRyTfbA0p3utfYkY5vEx5uP7XS8Ai1H5vMckBsegpJ6JmYEKDEDYo4Z0xATZmAUIt7pp1nVDzNX75N73Dafbgaurq0d/dnv1jReAuYD0hzZ9DwxqetYnM976RBqrF1Kuxsw4Xs/GHfWQS8PqlG9C0Cr+cmH2VeDSyqPOF4G49JLdIIJBdXddK++ECKaHdMwGKcLEvROCob+ryX0McKvoyb2dgEsrrwIdRjFoFZ110m+AsZSLA1qhkEQhQ9rApO1pDvQLAbeJDwYc5YBmrRAoHGHzCWeTHnY3Yz5DM4GbmqMJtDkcsTZMOjEELKJ/HQhYDMvn3YgIAg5lPeLRcAlBC367ErwWGWccDcQwbZNfkqhTvKNRXgG+V1BbS1H5+XxQNFPwnJqk3UfzZ+LycoUQ0uPek4Uc1lUthqVnaynF1O0aerx97I7A9i/aEudVg/GaFx0+HzaS5zBtpIr2E9VdY3ZHIB96VYJoDSLpyi2yOA0uKv8VF35fk/SZfycmzphaUcQQxw4EbC8IwsU0Vy9e+YW6+g7TpdO1IqPjUBryxTRysaIPl+8ORqvXlV56wRSvPtSFSjd0ocriQKSyNBCb+Tl+4dfKl5PqrkB9qPRA5aBAhuAsShYBS1XAGaVB5SCbChPBtYmGe/YT12YO/n922RWo8dOf9pkJ0DrIZ8GRKpuc+k49lJ1hraHcRp85VFdaIuve6jX0wECtj36gtITBk5xkOSD4UmXwJqcgNlZrmnzDIDcRDe+5pUMAA/QDhTkESnOIDefOgide5KAlsASzzV4DBn3WaMNZPSSQ11BpwlkvNQlEdgbIsfNgwbNNqcEHfbbDAv3jv5EjOGjR+H9jhdq6f6iyThUvrLvI4nOZMbChsMaeodMfOQ8ODE21NNS5EixvXXMwA2iMBlMg0+SN1WeLHc7KyPB3S/pQ5aYmWFpUeuhrnBvdUPmKH6v8k9x95SNjZPaaZ+6O4sB++HL5/N7npwFWj+4nt19yOEILaB7SsfzZH8RLS60/3pG22nHoWKa/2dtcTmXe+nEiYdpvQ7QncAs253SWMKUSKK127fHQUO9e0L2ArQE1BKk6ZbK6XiRqGCUSiPb3r/0+kZC2THGIKbdgswjyFqHXQ9Lt3ojbbI2kw7FBOZ1AqtV/XSEIxU7QPYEzBsMMD8xgGJtwOhtxu30jbDYDqdH8+bNdtNx3yucQpOTo7WV1IlHdKBFz2mn+tDXlTQ/g1/KAfkgIBK0U/47XPx1Qq4HSaP64Nj8v3WuDvq/b8ALcdu3EDxyO9BW/X8uDIsmiFqemuWytEEokVrFAqOtsl39lb9Pu3AY/KTv7LiPccuJIofIjWzgL2DD9FZEvPwnlyyz38/rt9gaaYY7tBGwN9uXL4uj49N9TUzXL9mmAonUmX2rEGEjFQ7nKqG+YHnNSOVRAc+vJ+SbNMCfevX53UwGC+yu2+ytaqx1PFKez2Wq166Cb8rW1teOPH/9t9732PS4p8OuyHT10K7YFuhKJyVOeMwhN1VwT6UvCsKkgIZ3jugwxI6XQqb5hH60a8VV4uReAvMW2gdDSsHWvSLhuSp0DIHMaWGPEDRq/FbQBG6gwM2iDdui161mp0wAKEl0RMNxh8NtJu0iASntK8/OYNosrBTFjNwHMsS13kePWX4oG+kBq1qyHx1KNZDnfIArJRiAXb0THhxsSo2pdZOgHWcCyLNDmg3OWkdDGYML31Bj3fW1OBZ8iSR+rTmFXtjSXh22/6kXUIDb2sz16BVijGNhJH6CJIHRzz/x7Cdcvwy0rAgXluu7KRcGbJ1lbIgCRcqYeGE+CkkQftgMliAqEWjnbrZODdyQC7lQIfOkoSAZV0KWRsny/DLeuCKzLF091Jx2xTgJJCpM2qjNsSvAtwtDCbSBhv8EPHAyizxxUgPWORFn3UIhrI6yN9LGmkOuZZJDTMGj5dL/T1aZRSPRmr1kDFhJjPZx2lqgXnMlga+oObtrmqIeVWrWgiNhXNgfy7vFy3TwxtXyxvxAalcfsN3pwZEGIGRd7gshijx9ZFPqNi63nALLA9/fTwcn/Ad8kXq/PfnhyAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Vertical flowchart of the target human-on-the-loop pipeline where an engineer maintains the spec, an agent pipeline produces tests, metrics and signals are monitored, and humans only intervene to sharpen the spec when signals indicate drift&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/c80f2debf476d2808829c21dd4a9e085/c5bb3/hotl-pipeline-mobile-dark.png&quot;
        srcset=&quot;/static/c80f2debf476d2808829c21dd4a9e085/ff46a/hotl-pipeline-mobile-dark.png 325w,
/static/c80f2debf476d2808829c21dd4a9e085/a6d36/hotl-pipeline-mobile-dark.png 650w,
/static/c80f2debf476d2808829c21dd4a9e085/c5bb3/hotl-pipeline-mobile-dark.png 680w&quot;
        sizes=&quot;(max-width: 680px) 100vw, 680px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;We will not pretend we are there. Three things we do not have yet:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a quality metric we trust enough to gate merges on (today, commits per step is a lagging proxy);&lt;/li&gt;
&lt;li&gt;automated drift detection that ties contract changes to affected SCNs and forces a regeneration;&lt;/li&gt;
&lt;li&gt;a flake-rate signal that distinguishes infrastructure noise from real regressions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is exactly the matrix&apos;s Stage 3 → Stage 4 transition on Verification &amp;#x26; Quality and Human-Agent Interaction, and it is the direction the &lt;a href=&quot;https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/&quot;&gt;Harness Model post&lt;/a&gt; frames in detail. We are doing the work. We are not writing the post yet that says we are finished.&lt;/p&gt;
&lt;p&gt;The shape we expect the next two months to take: more specialized skills upstream of test generation (a contract-drift detector, a domain-aggregate refiner), more outcome attribution downstream (a Runner that can tell &quot;the spec is wrong&quot; from &quot;the application code is wrong&quot; automatically), and a quality metric we trust enough to merge on. None of those alone is dramatic. Together, they collapse the number of approval gates the engineer has to walk through, and they do it without giving up the ability to catch a debug-endpoint shortcut before it ships.&lt;/p&gt;
&lt;h2 id=&quot;traps-when-moving-the-team-along-the-harness-maturity-stages&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#traps-when-moving-the-team-along-the-harness-maturity-stages&quot; aria-label=&quot;traps when moving the team along the harness maturity stages permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Traps when moving the team along the harness maturity stages&lt;/h2&gt;
&lt;p&gt;Moving from &quot;we have some AI-assisted coding&quot; to &quot;agents generate E2E tests from SCNs and contracts&quot; is not a single jump. It is a sequence of small moves along the dimensions in &lt;a href=&quot;https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/&quot;&gt;the maturity matrix&lt;/a&gt;. Six traps cost us the most time at the transitions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Documentation accuracy becomes load-bearing.&lt;/strong&gt; When humans were the only readers, &quot;mostly right&quot; docs were fine; people noticed contradictions and asked. Agents do not ask. They take the contract at face value. A field marked optional that is actually required produces a wrong test every time. The Stage 2 → Stage 3 transition is when documentation stops being a productivity aid and becomes correctness infrastructure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tribal knowledge becomes a correctness ceiling.&lt;/strong&gt; The OAuth bug came from tribal knowledge - &quot;everyone knows the debug endpoint is for debugging&quot; - that had never reached the contract. While humans wrote the tests, the gap was invisible because the author also knew. The moment agents wrote the tests, the gap surfaced as a wrong test in production CI. Stage 2 → Stage 3 again, on the Context Engineering dimension.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vibe-coded existing tests sabotage the new ones.&lt;/strong&gt; The unit and integration tests we inherited were technically high-coverage and business-low-value. When the agent reads &lt;code class=&quot;language-text&quot;&gt;tests/&lt;/code&gt; looking for examples of how the codebase asserts things, those tests teach the wrong style. We had to mark a chunk of them as &quot;do not learn from&quot; and write a &lt;code class=&quot;language-text&quot;&gt;CLAUDE.md&lt;/code&gt; section pointing the agent at the SCN-driven E2E suites as the canonical examples. This is the trap at Stage 3 → Stage 4 on Verification &amp;#x26; Quality.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;API contract drift between controller and OpenAPI spec.&lt;/strong&gt; Once the agent reads the OpenAPI spec instead of guessing from controller code, the controller stops being the source of truth - but only if you defend that move. The first time a developer in a hurry adds a query parameter in the controller and forgets the OpenAPI annotation, the agent writes a wrong test against the right code. We caught two of these in April and a pre-commit hook in the contract repository now blocks the drift. Stage 3 → Stage 4 on Context Engineering.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WireMock JSON mappings as first-class contract artifacts.&lt;/strong&gt; The first instinct, ours included, is to treat WireMock stub JSON as &quot;test fixtures&quot; and let each suite invent its own. After the second OAuth bug that came from a stub that was technically wrong about the external service&apos;s response shape, we moved the WireMock mappings into a curated directory, tagged each with the upstream contract version they represent, and started reviewing them on PRs alongside the application code. Stub correctness is contract correctness. Stage 3 → Stage 4 on Verification &amp;#x26; Quality.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Skipping sprint-aligned investment.&lt;/strong&gt; The most expensive trap is the one that does not look like a trap. The temptation, when the harness is taking shape, is to run a &quot;documentation sprint&quot; - three days of writing docs, then back to features. We tried it once. The docs were stale by week three. What works for us is sprint-aligned investment: every sprint, the harness gets one of its dimensions sharpened - a new skill, a contract clean-up, a Runner improvement. The pace is unglamorous; it is also the only pace that produced the curves above. Stage 1 → Stage 2 on Process and Workflow, and the trap that decides whether the whole journey starts at all.&lt;/p&gt;
&lt;p&gt;The matrix&apos;s brownfield reality check applies directly: stages 1 through 3 work on brownfield systems, but Stage 4 - systematic harness with mechanical verification - assumes a level of architectural cleanliness that legacy code rarely has. Our service was extracted from a larger one, with real organizational scars, and even then we needed ten weeks of foundation work before agent-generated tests were trustworthy. A genuinely legacy system would need that work to land first on a single bounded module before any agent-driven test generation made sense.&lt;/p&gt;
&lt;p&gt;The honest framing is this: harness engineering for E2E does not skip the boring work. It rewards teams that did the boring work, and it punishes teams that did not.&lt;/p&gt;
&lt;h2 id=&quot;for-monday---write-one-scn-sketch-one-aggregate&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#for-monday---write-one-scn-sketch-one-aggregate&quot; aria-label=&quot;for monday   write one scn sketch one aggregate permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;For Monday - write one SCN, sketch one aggregate&lt;/h2&gt;
&lt;p&gt;If you want a concrete next step that costs you nothing in tooling and an hour of attention, do two things.&lt;/p&gt;
&lt;p&gt;First, pick one critical business flow in your service that has no E2E test, and write the SCN. A scenario ID. Three preconditions. Three to six steps with an HTTP verb, an endpoint, and an expected status. You do not need an agent. You need a text editor and the discipline to be specific.&lt;/p&gt;
&lt;p&gt;Second, sketch the aggregate the SCN references - one paragraph. What are the states of the thing the scenario manipulates? Who owns each transition? What is the invariant the scenario implicitly assumes (the list contains only published entries, the user is authenticated, the configuration ID is distinct from the server URL)? You will discover, in the writing, what you do not yet know - and that is the answer to &quot;can the agent write this test for me.&quot;&lt;/p&gt;
&lt;p&gt;If the SCN comes out clean and the aggregate is one paragraph you would defend at a review, you have the shape of the spec the harness rewards. If either falls apart, fix the documentation that has to exist for them to make sense. Then come back to the SCN. Then, and only then, ask the agent.&lt;/p&gt;
&lt;p&gt;This is the bet we are making, three and a half months and 162 steps in: better specs produce better tests; the spec corpus compounds; the harness is the system that holds the compounding together. The flat CI curve, the five-fold drop in commits per step, the fact that a 20-step OAuth re-auth scenario now lands in a single commit - none of that came from a smarter model. It came from documentation an agent could read, an aggregate small enough to keep accurate, a scenario format that respects the agent&apos;s lane, and a discipline that points every fix at the spec.&lt;/p&gt;
&lt;p&gt;If you can&apos;t write the scenario, the agent can&apos;t either. Write one this week. Sketch the aggregate next to it. See what you learn from the writing alone.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[The Harness Model - AI Engineering Maturity Matrix, Q1 2026]]></title><description><![CDATA[Most organizations measure AI adoption by which models or tools they use. That tells you almost nothing about whether those tools produce…]]></description><link>https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2026/the-harness-model-ai-engineering-maturity-matrix/</guid><pubDate>Thu, 16 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/79cce25f6fba0511c48a501974c25b10/71c1d/harness-board.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAACq0lEQVR42l2Q309SYRjHzz/QutORy+qi5pYK4c/yotxMkIF0UNQtg/BHmE6XU2dwYV3ZNNM1a5SpF1bodLgMa61szZscxoFDCjjNJTgB0ZkcUoHznnN6z0G96N1nz/t8n/f57n32IDSIcsRoKg7JcIemAcxZaIqTNHXYQ7IR5iCKwItiIeMPAJAzUyZjf992wAct0OZcXBgfM3q8HoahOD955I8ix5qMRWDr1Ovh1qKszhrUveCIjzA2MvRKrymVFtoXFqEEZPTQD2LI8bdx86cJo0FX/cM8uL7qjptnzWPm4W5dTQlutUBJ/m8mIyAWgRP7HRZHV2P91QwJP+3bRzPbSjPj46M9atGLTt0jMza34qNAFH5OcZtCKDZjl7TutFn0CqcieTL/1OO0k/PtFXBJvmXc0NdVpu8X3H+bem/oekvvDLbEDg8ANCI0FV39ter4Pvf56+wbQ6/pSfOgqsBwS2KolU+PPJt4N/V+oPvBS9OFpoHalrbeVrVxwoQ78KXlFYYGyP7fUHlDh/Ba6WWpViMrTq9E+UWarApteom2qqlRevehoLLwPJpSoFD0dbXJVFUDzzs+jOh7nnYSIQKZxzAh2pCK1ueJVHXlStFtNV9WnyW/ky5WZ96oy0O1AqXy4s3ibGllWY1OUt0uROvkmiaRqhmz48ioaToxQ3LuspyXKU7ILUngi5JzZWeuoEkZ4tPZ0qSc4uQcxdkcJU8oTsyUJApEvEsiXrb8REr+pPkL4vm9ZrH+tNqdEBvuxOwuK+6y2l2Yw80lTjbCOu6yOdw2rojZnfPYonfNg0T+BJmDEBMJM7E9Buwx1AFHhI3gKDkGVsA+Q+4x0XCM2Ea2vGs7vvXdgC+06Q9vBcLbmxBia5MI+olggIAVFigPIyQU9O8GNnY2vP8ANWI3QJYhWrcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Harness Console With Two Architects&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/79cce25f6fba0511c48a501974c25b10/3c492/harness-board.png&quot;
        srcset=&quot;/static/79cce25f6fba0511c48a501974c25b10/ff46a/harness-board.png 325w,
/static/79cce25f6fba0511c48a501974c25b10/a6d36/harness-board.png 650w,
/static/79cce25f6fba0511c48a501974c25b10/3c492/harness-board.png 1300w,
/static/79cce25f6fba0511c48a501974c25b10/71c1d/harness-board.png 1536w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Most organizations measure AI adoption by &lt;a href=&quot;https://handsonarchitects.com/blog/2026/ai-toolset-for-software-architect-2026q1/&quot;&gt;which models or tools they use&lt;/a&gt;. That tells you almost nothing about whether those tools produce reliable outcomes. The teams winning with AI are not the ones with the best models. They are the ones who have stopped treating AI output as a deliverable and started treating it as an unverified input to a system that catches errors.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The discipline does not disappear - it relocates.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This post introduces &lt;strong&gt;The Harness Model&lt;/strong&gt;, a maturity matrix for AI engineering practices across ten dimensions. It is not a certification framework or a prescriptive roadmap. It is a diagnostic tool - a structured way for engineering teams to assess where they are and decide where to invest next.&lt;/p&gt;
&lt;p&gt;The visual format is inspired by the &lt;a href=&quot;https://container-solutions.com/cloud-native-maturity-matrix/&quot;&gt;Cloud Native Maturity Matrix&lt;/a&gt; by Container Solutions: same dimension-based grid approach, applied to AI engineering practices. This is a &lt;strong&gt;Q1 2026 snapshot&lt;/strong&gt;. The field is moving fast. We expect to revise this quarterly as practices evolve and community feedback arrives.&lt;/p&gt;
&lt;h2 id=&quot;the-harness-thesis&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-harness-thesis&quot; aria-label=&quot;the harness thesis permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Harness Thesis&lt;/h2&gt;
&lt;p&gt;A convergence is happening across the AI engineering community: when AI writes the code, the craft shifts to designing the system that controls AI. Multiple practitioners have arrived at this insight from different angles. The individual concepts - harness engineering, context engineering, loop progression - have been described separately. Our contribution is the synthesis: a structured assessment model that maps where a team stands across all these dimensions simultaneously.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;harness&lt;/strong&gt; is the system of context, constraints, and verification that wraps an AI agent. It is what turns raw model output into something you can trust. The harness includes the documentation agents read, the linters that enforce boundaries, the tests that catch regressions, and the feedback loops that improve all of these over time.&lt;/p&gt;
&lt;p&gt;Mitchell Hashimoto &lt;a href=&quot;https://mitchellh.com/writing/my-ai-adoption-journey#step-5-engineer-the-harness&quot;&gt;described&lt;/a&gt; the core practice: anytime an agent makes a mistake, you engineer a solution so it never makes that mistake again. He called this &lt;strong&gt;harness engineering&lt;/strong&gt;. OpenAI&apos;s team &lt;a href=&quot;https://openai.com/index/harness-engineering/&quot;&gt;reported&lt;/a&gt; a striking demonstration: over five months, three engineers used Codex to build a million-line codebase with zero manually written code - a dogfooding result from the team that built the agent, not yet reproduced by customer teams at that scale. Their harness had three layers - context engineering that made the repository itself the system of record, architectural enforcement through custom linters and structural tests, and garbage collection agents that fought entropy before it compounded.&lt;/p&gt;
&lt;p&gt;Birgitta Boeckeler &lt;a href=&quot;https://martinfowler.com/articles/exploring-gen-ai/harness-engineering.html&quot;&gt;observed&lt;/a&gt; that harnesses might become the new service templates - standardized starting points for common application topologies. She also raised the critical brownfield question: which harnessing techniques work for existing applications, and which only work for systems built with a harness in mind?&lt;/p&gt;
&lt;p&gt;Chad Fowler &lt;a href=&quot;https://aicoding.leaflet.pub/3mbrvhyye4k2e&quot;&gt;placed&lt;/a&gt; this in historical context: when XP replaced phase-gate development, rigor relocated from documents to tests. The formula now: &lt;strong&gt;probabilistic inside, deterministic at the edges&lt;/strong&gt; - let AI generate freely within boundaries that are strict, explicit, and mechanically enforced.&lt;/p&gt;
&lt;p&gt;Kief Morris &lt;a href=&quot;https://martinfowler.com/articles/exploring-gen-ai/humans-and-agents.html&quot;&gt;mapped&lt;/a&gt; the progression of how humans relate to agents. Start &lt;strong&gt;outside the loop&lt;/strong&gt; (vibe coding - agent runs, human hopes). Move to &lt;strong&gt;in the loop&lt;/strong&gt; (human reviews every line). Then &lt;strong&gt;on the loop&lt;/strong&gt; (human builds the harness that controls agents). Finally, the &lt;strong&gt;agentic flywheel&lt;/strong&gt; (human directs agents to improve the harness itself). Each step shifts human effort from execution to system design - from writing code to writing the system that writes code.&lt;/p&gt;
&lt;p&gt;Taken together, the five practitioners describe different entry points into the same conclusion: reliable AI outcomes require a designed system around the agent, not a better agent.&lt;/p&gt;
&lt;p&gt;The better your harness, the more you can trust the output. The worse your harness, the more time you spend reviewing, fixing, and re-prompting - which is often slower than writing the code yourself. &lt;a href=&quot;https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/&quot;&gt;METR&apos;s 2025 RCT&lt;/a&gt; found experienced open-source developers using AI tools were 19% slower on average, while believing they were faster. The harness thesis is exactly what explains that gap: without the system around the agent, the review-fix-reprompt tax dominates.&lt;/p&gt;
&lt;p&gt;One principle runs through every stage of this model: &lt;strong&gt;accountability stays with the human&lt;/strong&gt;. Maturity is not a transfer of responsibility to agents; it is the humans earning the right to delegate more execution because the system around the agents has been engineered to catch what they get wrong. When an agent ships a bug, it is still a human who answers for it - and that framing should shape every decision about where to invest next.&lt;/p&gt;
&lt;p&gt;A related principle shapes the matrix itself: &lt;strong&gt;agents are first-class team members&lt;/strong&gt;. If a piece of information - a backlog item, an architectural decision, a runbook, a metric - is available to humans but not to agents, the harness has a hole in it. &quot;It sits in my slide deck&quot; is the anti-pattern. Everything the team works from should be equally reachable by the humans and the agents on that team.&lt;/p&gt;
&lt;h2 id=&quot;the-maturity-model&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-maturity-model&quot; aria-label=&quot;the maturity model permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Maturity Model&lt;/h2&gt;
&lt;p&gt;The Harness Model defines five stages across ten dimensions. Each stage describes a dominant interaction paradigm between humans and AI agents - not a maturity score to optimize. Most organizations today are at Stage 1 or 2. Stages 4 and 5 are aspirational endpoints, not expected norms. Stage 5 is our informed speculation - no validated production examples exist yet, and we included it because extrapolating where this is heading was too interesting to leave out.&lt;/p&gt;
&lt;p&gt;We organize the ten dimensions into four clusters that form a causal story: &lt;strong&gt;Foundation&lt;/strong&gt; (inputs - what agents work with), &lt;strong&gt;Governance&lt;/strong&gt; (constraints - what keeps agents safe), &lt;strong&gt;Delivery&lt;/strong&gt; (execution - how work gets done), and &lt;strong&gt;Outcomes &amp;#x26; Learning&lt;/strong&gt; (measurement and evolution - how results are assessed and how the system improves). Reading the matrix top-to-bottom follows this causal flow - inputs feed into constraints, which shape execution, which produces outcomes that feed the next iteration - while reading left-to-right shows progression through the five maturity stages.&lt;/p&gt;
&lt;p&gt;A candid note before the table: we typically start greenfield teams at Stage 3 and have reached some Stage 4 characteristics in select dimensions - never all at once. If that sounds modest, it is meant to.&lt;/p&gt;
&lt;div class=&quot;cluster-legend&quot; role=&quot;list&quot; aria-label=&quot;Matrix cluster legend&quot;&gt;
  &lt;div class=&quot;cluster-legend__item&quot; role=&quot;listitem&quot;&gt;&lt;span class=&quot;cluster-dot cluster-dot--foundation&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt;&lt;strong&gt;Foundation&lt;/strong&gt;&lt;/div&gt;
  &lt;div class=&quot;cluster-legend__item&quot; role=&quot;listitem&quot;&gt;&lt;span class=&quot;cluster-dot cluster-dot--governance&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt;&lt;strong&gt;Governance&lt;/strong&gt;&lt;/div&gt;
  &lt;div class=&quot;cluster-legend__item&quot; role=&quot;listitem&quot;&gt;&lt;span class=&quot;cluster-dot cluster-dot--delivery&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt;&lt;strong&gt;Delivery&lt;/strong&gt;&lt;/div&gt;
  &lt;div class=&quot;cluster-legend__item&quot; role=&quot;listitem&quot;&gt;&lt;span class=&quot;cluster-dot cluster-dot--outcomes&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt;&lt;strong&gt;Outcomes &amp;amp; Learning&lt;/strong&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;table-wide harness-matrix&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;No AI Process&lt;/th&gt;
&lt;th&gt;Chatbot-Assisted&lt;/th&gt;
&lt;th&gt;Human-in-the-Loop&lt;/th&gt;
&lt;th&gt;Systematic Harness&lt;/th&gt;
&lt;th&gt;Agentic Flywheel&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context Engineering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Copy-paste snippets into chat&lt;/td&gt;
&lt;td&gt;Human maintains AGENTS.md; manual context loading&lt;/td&gt;
&lt;td&gt;Repository as system of record; progressive disclosure&lt;/td&gt;
&lt;td&gt;Human designs context structure; agents maintain and evolve it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Team (Humans + Agents)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Large specialist team; humans execute; standard tools&lt;/td&gt;
&lt;td&gt;Same specialist team; humans execute with AI suggestions&lt;/td&gt;
&lt;td&gt;Leaner delivery-minded team; agents use file/terminal&lt;/td&gt;
&lt;td&gt;Small generalist team per initiative; agents run full stack&lt;/td&gt;
&lt;td&gt;Same generalists span multiple initiatives; agents self-provision&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security &amp;#x26; Trust&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No AI-specific security concerns&lt;/td&gt;
&lt;td&gt;Human reviews AI output for obvious issues&lt;/td&gt;
&lt;td&gt;AI output runs through existing SAST/DAST tooling&lt;/td&gt;
&lt;td&gt;Scoped, auditable agent access; AI-specific threat modeling&lt;/td&gt;
&lt;td&gt;Agents enforce and evolve policies; human governs trust boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Architectural Governance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No AI constraints&lt;/td&gt;
&lt;td&gt;Basic rules in prompt files&lt;/td&gt;
&lt;td&gt;Human-enforced boundaries; constrained solution space&lt;/td&gt;
&lt;td&gt;Custom linters with remediation; taste invariants as code&lt;/td&gt;
&lt;td&gt;Harnesses as org-wide templates; human defines taste&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Human-Agent Interaction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Chatbot Q&amp;#x26;A&lt;/td&gt;
&lt;td&gt;Agent generates; human reviews every output line-by-line&lt;/td&gt;
&lt;td&gt;Human on the loop; builds harness&lt;/td&gt;
&lt;td&gt;Agents propose harness improvements; human directs evolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Workflow &amp;#x26; Process&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No AI in workflow&lt;/td&gt;
&lt;td&gt;Occasional AI for specific tasks&lt;/td&gt;
&lt;td&gt;Daily agent use; human delegates parallel work&lt;/td&gt;
&lt;td&gt;Always-running agents; agent-first; human sets priorities&lt;/td&gt;
&lt;td&gt;Agents handle full delivery cycle; human steers outcomes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reliability &amp;#x26; Operations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual runbooks; engineers triage and mitigate incidents&lt;/td&gt;
&lt;td&gt;Same ops team; agents draft queries; humans mitigate&lt;/td&gt;
&lt;td&gt;Structured runbooks; agents analyze alerts; humans mitigate&lt;/td&gt;
&lt;td&gt;Agents auto-triage low-severity alerts; humans handle critical incidents&lt;/td&gt;
&lt;td&gt;Agents auto-remediate known failures; humans own novel incidents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Verification &amp;#x26; Quality&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual testing&lt;/td&gt;
&lt;td&gt;Agent runs tests; human checks&lt;/td&gt;
&lt;td&gt;Custom linters and structural tests; human reviews results&lt;/td&gt;
&lt;td&gt;Agent-to-agent review with quality scoring; human defines criteria&lt;/td&gt;
&lt;td&gt;Agents detect and fix regressions; human defines standards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Knowledge &amp;#x26; Feedback Loops&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tribal knowledge; docs in wikis&lt;/td&gt;
&lt;td&gt;README-level docs&lt;/td&gt;
&lt;td&gt;Human structures docs in repo; retrospectives capture agent lessons&lt;/td&gt;
&lt;td&gt;Versioned plans and quality grades in repo; agent failures feed the harness&lt;/td&gt;
&lt;td&gt;Agents maintain docs and capture learnings; human curates strategy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Planning &amp;#x26; Decision-Making&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual boards; experience-driven decisions&lt;/td&gt;
&lt;td&gt;AI helps write tickets and research options&lt;/td&gt;
&lt;td&gt;Agent triages issues; human prioritizes; decisions validated via PoCs&lt;/td&gt;
&lt;td&gt;Decision signals equally reachable by humans and agents; humans validate with cheap prototypes&lt;/td&gt;
&lt;td&gt;Agents propose initiatives; humans set direction from measured results&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Most teams today are at Stage 1 or 2. Stage 4 is aspirational; Stage 5 is informed speculation with no validated production examples yet.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;foundation-context-and-team&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#foundation-context-and-team&quot; aria-label=&quot;foundation context and team permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Foundation: Context and Team&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Context Engineering&lt;/strong&gt; is how effectively agents receive the right information at inference time. The progression moves from copy-pasting snippets into a chat window to making the repository itself the single source of truth - with &lt;strong&gt;progressive disclosure&lt;/strong&gt; (agents start with a map of the repo and pull in detailed documents only when a task needs them, rather than loading an encyclopedia up front). The key transition: at Stage 3 a human manually curates &lt;code class=&quot;language-text&quot;&gt;AGENTS.md&lt;/code&gt;. At Stage 4, the repository structure &lt;em&gt;is&lt;/em&gt; the context, and documentation linters enforce that it stays current.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Team (Humans + Agents)&lt;/strong&gt; describes the team as a single composition of humans &lt;em&gt;and&lt;/em&gt; agents, because treating agents as second-class citizens is exactly the framing this model rejects. The row moves along two axes simultaneously: &lt;strong&gt;team shape&lt;/strong&gt; (large specialist → business-oriented delivery → small generalist per initiative → generalist across multiple initiatives) and &lt;strong&gt;agent capability tier&lt;/strong&gt; (none → suggestion-only → file/terminal access → ephemeral full-stack environments with browser automation and local observability → agents self-provisioning their own environments). Early pioneers built the full-stack environments from scratch - OpenAI&apos;s integration of the Chrome DevTools Protocol, which lets agents drive a real browser alongside code execution, is one example.&lt;/p&gt;
&lt;p&gt;The human role evolves along this dimension from execution, to delivery leadership, to multi-initiative oversight. A nuance worth stating plainly: &quot;AI provides specialist depth on demand&quot; is only half true. Agents genuinely provide &lt;strong&gt;technical depth&lt;/strong&gt; - frameworks, patterns, languages - on demand. They do &lt;em&gt;not&lt;/em&gt; replace &lt;strong&gt;domain depth&lt;/strong&gt;: the unique business rules, customer workflows, and market dynamics that give a product its edge. In complex-domain businesses, architects need to move &lt;em&gt;toward&lt;/em&gt; close customer relationships as agents absorb execution work, not away from them. This is where the &quot;human role evolves, never disappears&quot; principle is most visible.&lt;/p&gt;
&lt;p&gt;The Stage 3→4 jump is where both axes move together: teams get leaner &lt;em&gt;because&lt;/em&gt; agent capability crosses the full-stack threshold. The trade-off is real - you pay for that leaner team with investment in the platform agents now run on (ephemeral environments, browser automation, observability) &lt;em&gt;and&lt;/em&gt; in the governance that makes that leverage safe. Teams that try to shrink without paying either cost end up with the same number of humans and less agent trust.&lt;/p&gt;
&lt;h3 id=&quot;governance-security-and-architecture&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#governance-security-and-architecture&quot; aria-label=&quot;governance security and architecture permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Governance: Security and Architecture&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Security &amp;#x26; Trust&lt;/strong&gt; governs how agent access, AI-generated code security, and supply chain integrity are managed. The progression follows a Zero Trust model: start with no AI-specific concerns, move through existing security tooling - SAST (Static Application Security Testing) and DAST (Dynamic Application Security Testing) applied to agent output just like human-written code - and eventually reach scoped, auditable agent access with AI-specific threat modeling. At higher stages, supply chain verification for agent tools becomes critical - treat tool descriptions and AI skills as potential attack vectors, not just convenience features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Architectural Governance&lt;/strong&gt; defines how constraints and design &lt;strong&gt;taste&lt;/strong&gt; are enforced on agents. We use &lt;em&gt;taste&lt;/em&gt; in the sense of refined judgment about trade-offs: which abstractions age well, which coupling is acceptable, which patterns reduce long-term risk. In an AI era where generation is cheap, taste is emerging as the new moat - an intangible, durable advantage that survives when everyone has the same models. &quot;Taste invariants as code&quot; means encoding that judgment into linters and structural tests so agents cannot unknowingly violate it.&lt;/p&gt;
&lt;p&gt;Basic prompt rules give way to human-enforced boundaries, then to custom linters with remediation instructions injected directly into agent context. The OpenAI team&apos;s practice is instructive: they enforce module boundaries and data shape validation mechanically, but leave implementation choices to the agent. At Stage 5, harnesses would become organizational templates - the new golden path - and the human&apos;s central job would be &lt;em&gt;defining&lt;/em&gt; the taste the harness enforces.&lt;/p&gt;
&lt;p&gt;The Stage 3→4 jump is where the cost shows up: turning tribal taste into enforced linters requires senior engineers to name and codify judgment they have always held implicitly. That is slow work, and teams that skip it end up at Stage 3 forever - agents inside boundaries the humans cannot articulate, held together by constant review.&lt;/p&gt;
&lt;h3 id=&quot;delivery-interaction-workflow-reliability--operations&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#delivery-interaction-workflow-reliability--operations&quot; aria-label=&quot;delivery interaction workflow reliability  operations permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Delivery: Interaction, Workflow, Reliability &amp;#x26; Operations&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Human-Agent Interaction&lt;/strong&gt; is the division of labor and trust boundary between humans and agents. This maps directly to Morris&apos;s loop progression: from chatbot Q&amp;#x26;A (outside the loop) through line-by-line review (in the loop) to building the harness (on the loop) and eventually directing agents that improve the harness (flywheel). The common failure at Stage 3: teams that review every line of generated code become a bottleneck and conclude AI &quot;doesn&apos;t save time.&quot; Remember that accountability stays with the human at every stage - the loop describes &lt;em&gt;where&lt;/em&gt; the human engages, not whether they are on the hook.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Workflow &amp;#x26; Process&lt;/strong&gt; describes how humans and agents coordinate daily work. Hashimoto&apos;s journey is a practical illustration: from occasional chatbot use to &quot;always have an agent running.&quot; The progression from &quot;occasional AI for specific tasks&quot; to &quot;agent-first delivery&quot; changes what engineering management looks like. At Stage 4, the question is not &quot;should we use AI for this?&quot; but &quot;why would a human do this instead of an agent?&quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reliability &amp;#x26; Operations&lt;/strong&gt; covers what happens &lt;em&gt;after&lt;/em&gt; deployment - runbooks, alerts, incident triage, mitigation, and remediation. If Verification &amp;#x26; Quality asks &quot;did we build it right?&quot;, this dimension asks &quot;is it running right?&quot;&lt;/p&gt;
&lt;p&gt;Two axes progress together along this row. The &lt;strong&gt;runbook axis&lt;/strong&gt; moves from tribal knowledge, to markdown-in-Git, to LLM-ready structured runbooks, to agent-executable playbooks - a senior SRE&apos;s ability to read dense observability queries becomes a shared capability as agents draft and simplify queries for the rest of the team. The &lt;strong&gt;alert-handling axis&lt;/strong&gt; moves from manual query writing, through AI-drafted queries, to auto-analysis on alert that surfaces initial context within minutes, to severity-based auto-triage with human checkpoints on P1/P2 incidents, and finally to proactive anomaly detection that mitigates before customer impact.&lt;/p&gt;
&lt;p&gt;The human role shifts from executing incidents, to mitigating with AI assist, to handling only critical incidents, to owning only novel failure modes. The Stage 3→4 jump here is not &quot;agents get smarter&quot; - it is &quot;agents get trusted with low-severity triage end-to-end.&quot; That is a governance decision backed by better runbooks and observability, not blind delegation, and it reflects the principle that maturity is trust &lt;em&gt;earned&lt;/em&gt;, not control removed.&lt;/p&gt;
&lt;h3 id=&quot;outcomes--learning-verification-knowledge-planning&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#outcomes--learning-verification-knowledge-planning&quot; aria-label=&quot;outcomes  learning verification knowledge planning permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Outcomes &amp;#x26; Learning: Verification, Knowledge, Planning&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Verification &amp;#x26; Quality&lt;/strong&gt; is how correctness is judged in agent-produced output. Early stages rely on human eyes. Later stages shift to programmatic verification - custom linters, structural tests, agent-to-agent review with quality scoring. Fowler&apos;s principle applies here: if generation gets easier, judgment must get stricter. A team at Stage 4 has encoded &quot;what good looks like&quot; into tooling, not tribal knowledge.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Knowledge &amp;#x26; Feedback Loops&lt;/strong&gt; tracks how teams capture, maintain, and evolve shared knowledge from agent interactions. The critical shift is from docs in wikis to versioned artifacts in the repository. At Stage 4, plans are first-class artifacts with progress and decision logs. Agent failures generate systematic feedback that improves the harness. Important data points - quality grades, benchmark results, regression signals - live in the repo so results from one agent iteration can be compared against previous iterations. Knowledge that lives only in Slack threads or people&apos;s heads effectively does not exist for agents.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Planning &amp;#x26; Decision-Making&lt;/strong&gt; shifts from heuristic-driven decisions and manual ticket boards to data-informed, continuously validated approaches. The underlying principle is &lt;em&gt;equal access&lt;/em&gt;: every signal a human uses to make decisions - the backlog, metrics, logs, traces, fitness functions, customer feedback - must be equally reachable by agents. At Stage 4, agents propose and score items against those signals; humans validate the important decisions by building cheap prototypes and measuring real outcomes. The shift is from &quot;we think this is right&quot; to &quot;we built something small and measured.&quot;&lt;/p&gt;
&lt;p&gt;As team composition becomes more &lt;a href=&quot;https://martinfowler.com/articles/expert-generalist.html#TheCharacteristicsOfAnExpertGeneralist&quot;&gt;generalist&lt;/a&gt;, the Outcomes &amp;#x26; Learning cluster is what prevents that generalism from becoming shallow: the measurement and feedback machinery is where taste is refined and where agents earn the trust they are given.&lt;/p&gt;
&lt;h3 id=&quot;brownfield-reality-check&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#brownfield-reality-check&quot; aria-label=&quot;brownfield reality check permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Brownfield Reality Check&lt;/h3&gt;
&lt;p&gt;All the source articles lean greenfield. Stages 1 through 3 apply equally to brownfield and greenfield systems - chatbot use, human review, and basic linting work regardless of codebase age. Divergence starts at Stage 4.&lt;/p&gt;
&lt;p&gt;First, a definition. When we say &quot;brownfield&quot; in this article we mean the common case: a legacy codebase carrying real tech debt, with patchy documentation, weak automation, and processes shaped more by history than design. A well-maintained long-lived codebase is a different beast. A well-maintained codebase can &lt;em&gt;accelerate&lt;/em&gt; agents - its established templates and copy-paste patterns give them more to imitate than a greenfield project where everything is built from scratch. To be clear, we believe that the blocker is not age, but rather the lack of a mature codebase (debt + disorder).&lt;/p&gt;
&lt;p&gt;Systematic harness engineering assumes a level of architectural cleanliness that debt-laden legacy codebases rarely have. Module boundaries need to be clear. Dependency directions need to be enforceable. Test coverage needs to be sufficient for agents to verify their own work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Harness-readiness checklist for brownfield systems.&lt;/strong&gt; Before building a harness on top of a legacy codebase, work through these three questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Modularization&lt;/strong&gt;: Can you name, in a sentence, what each module owns? If ownership is ambiguous, agents will generate code that belongs nowhere and everywhere.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Boundary enforcement&lt;/strong&gt;: Are module dependencies explicit and directional? If not, agents will create new coupling faster than humans can untangle it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test coverage&lt;/strong&gt;: Can agents verify their own changes without human intervention? Gaps in coverage become gaps in agent autonomy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Teams that skip harness-readiness and jump straight to agentic workflows typically regress within weeks - agent-generated entropy compounds faster than manual cleanup can address it. This is why Stage 4 remains largely unvalidated for brownfield systems as of Q1 2026. What we know works: Stages 1-3 apply equally, and AI can accelerate the harness-readiness work itself. Boeckeler&apos;s analogy is apt: it is like running a static analysis tool on a codebase that has never had one. You will drown in alerts unless you tame the foundation first.&lt;/p&gt;
&lt;h3 id=&quot;mixed-maturity-and-regression&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mixed-maturity-and-regression&quot; aria-label=&quot;mixed maturity and regression permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Mixed Maturity and Regression&lt;/h3&gt;
&lt;p&gt;We have seen this pattern firsthand: one team scored Stage 4 on Context Engineering - structured &lt;code class=&quot;language-text&quot;&gt;AGENTS.md&lt;/code&gt; files, repo-level documentation wired into every prompt - yet sat at Stage 2 on Verification, relying entirely on manual code review to catch agent mistakes. In practice, this looked like agents producing architecturally sound code that still broke integration tests nobody ran automatically. Once the team prioritized automated verification - wiring CI checks into the agent feedback loop - their context investment finally compounded: agents could self-correct instead of waiting for a human to spot failures. The weaker dimension was the bottleneck, not the stronger one.&lt;/p&gt;
&lt;p&gt;Mixed maturity across dimensions is the norm, not a failure. When prioritizing, start with the dimension that creates the most pain or blocks progress in others.&lt;/p&gt;
&lt;p&gt;Regression is also real. Last year, many teams stuck at Stage 2 abandoned AI entirely and regressed to Stage 1. This year, the most common struggle is overcoming Stage 3 - teams have justified fear of giving too much control to AI without sufficient verification and governance in place. The harness is exactly what addresses that fear: you do not hand over control, you build the system that makes delegation safe.&lt;/p&gt;
&lt;h2 id=&quot;where-are-you-a-conversation-starter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#where-are-you-a-conversation-starter&quot; aria-label=&quot;where are you a conversation starter permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Where Are You? A Conversation Starter&lt;/h2&gt;
&lt;p&gt;This model is not a certification. It is a diagnostic tool - best used as a team conversation starter, not a solo assessment. Grab a whiteboard, walk through the dimensions, and see where disagreements surface. Those disagreements are the signal. A practical starting point: circulate the matrix to your senior engineers before your next staff meeting. Ask each person to score one or two dimensions independently. Bring the disagreements to the meeting - you do not need consensus, you need the conversation.&lt;/p&gt;
&lt;h3 id=&quot;foundation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#foundation&quot; aria-label=&quot;foundation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Foundation&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;What does your agent read before it starts a task? A single &lt;code class=&quot;language-text&quot;&gt;AGENTS.md&lt;/code&gt;? Structured documentation? The repo itself?&lt;/li&gt;
&lt;li&gt;Is every piece of information your humans rely on equally reachable by your agents, or does some of it live in slide decks, private notes, and DM threads?&lt;/li&gt;
&lt;li&gt;If you removed one specialist from the team, could agents cover the &lt;em&gt;technical&lt;/em&gt; gap today? And who is close enough to the customer to cover the &lt;em&gt;domain&lt;/em&gt; gap?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;governance&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#governance&quot; aria-label=&quot;governance permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Governance&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;What can your AI agent access? Is that access scoped and auditable, or &quot;whatever the developer&apos;s token allows&quot;?&lt;/li&gt;
&lt;li&gt;If an agent violates an architectural boundary, how long before someone notices? Minutes, days, or the next quarterly review?&lt;/li&gt;
&lt;li&gt;Which pieces of your design taste are encoded as linters or structural tests, and which still live only in senior engineers&apos; heads?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;delivery&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#delivery&quot; aria-label=&quot;delivery permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Delivery&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;How much of your day involves an agent running on a task? Zero percent? Ten? Fifty?&lt;/li&gt;
&lt;li&gt;When an incident fires at 3am, does an agent surface initial context within minutes, or does a human start from a blank terminal?&lt;/li&gt;
&lt;li&gt;When an agent ships something that breaks in production, is it clear that the human on call still owns the outcome?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;outcomes--learning&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#outcomes--learning&quot; aria-label=&quot;outcomes  learning permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Outcomes &amp;#x26; Learning&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Could a new team member find your verification criteria in code, or only in someone&apos;s head?&lt;/li&gt;
&lt;li&gt;Where does your team&apos;s knowledge live - in Slack threads, wiki pages, or versioned artifacts in the repository?&lt;/li&gt;
&lt;li&gt;Are the signals you use to decide what to build next - metrics, traces, fitness functions, customer feedback - equally available to your agents?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are not sure which stage your team is at for a given dimension, that ambiguity is itself a signal. It usually means you are between stages - and the uncertainty points to where investment would have the most impact.&lt;/p&gt;
&lt;p&gt;As mentioned earlier, we typically start greenfield teams at Stage 3 and have reached some Stage 4 characteristics in select dimensions. For brownfield teams, getting to Stage 2 is usually straightforward, but moving to Stage 3 takes hard work from the whole team - and so far we have not progressed beyond that for brownfield systems. If that sounds familiar, you are in good company.&lt;/p&gt;
&lt;h2 id=&quot;start-where-you-are&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#start-where-you-are&quot; aria-label=&quot;start where you are permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Start Where You Are&lt;/h2&gt;
&lt;p&gt;The harness is the through-line. Whether you are writing your first &lt;code class=&quot;language-text&quot;&gt;AGENTS.md&lt;/code&gt; or building custom linters with remediation instructions, you are doing harness engineering. The question is not whether to build a harness. It is how deliberately you build it - and remembering, at every stage, that the accountability for the outcome still lives with a human.&lt;/p&gt;
&lt;p&gt;This is a Q1 2026 snapshot - a starting point that will evolve as practices mature and community feedback arrives. Pick one dimension where you feel the most pain. Identify what stage you are at and what the next stage looks like. Build one thing that moves you forward. That is harness engineering. If your team is just getting started, our guide on &lt;a href=&quot;https://handsonarchitects.com/blog/2026/how-to-start-with-ai-assisted-development-today/&quot;&gt;how to start with AI-assisted development&lt;/a&gt; covers the practical first steps.&lt;/p&gt;
&lt;h2 id=&quot;compare-notes-with-us&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#compare-notes-with-us&quot; aria-label=&quot;compare notes with us permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Compare notes with us&lt;/h2&gt;
&lt;p&gt;We&apos;re collecting patterns across teams to inform the Q2 2026 update of the Harness Model. Whether you ran the full assessment or just found one dimension that matched your team, we&apos;d like to compare notes - and we&apos;ll share back what we&apos;re seeing across teams in the Q2 update.&lt;/p&gt;
&lt;p&gt;Email us at &lt;a href=&quot;mailto:contact@handsonarchitects.com?subject=Harness%20Model%20%E2%80%94%20compare%20notes&quot;&gt;contact@handsonarchitects.com&lt;/a&gt; or DM either of us on LinkedIn: &lt;a href=&quot;https://www.linkedin.com/in/maciej-laskowski&quot;&gt;Maciej&lt;/a&gt; · &lt;a href=&quot;https://www.linkedin.com/in/tomaszmichalak&quot;&gt;Tomasz&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to Start using AI in Software Development Today]]></title><description><![CDATA[Most engineers we talk to are not skeptical about AI-assisted development. They are interested but stuck. They are waiting for the right…]]></description><link>https://handsonarchitects.com/blog/2026/how-to-start-with-ai-assisted-development-today/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2026/how-to-start-with-ai-assisted-development-today/</guid><pubDate>Mon, 16 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/f31a700d1e3c53f2859abe258b6ec78d/71c1d/how-to-start.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAADJElEQVR42gEZA+b8ADiUzEV4nYW41Kbi+4C1zXqluoqtxKrE2b3J1M/O0dXOztnSzt3Sy97OxN7TytbNxLu0rNXFutfdzdDbzQBDh7FjamRhjZtqaWp1OBZqKgatn5rh5vXU2urr7fPK0NzY3uf46Ob+1cz/5N/KycpdRjlmVVKXq6+/2NQAm7a4t7i5lrfIazEUzpJkto1sr8DHi+b75+Gc4+e5zN6i5uTp7Ojmy7Su/d3Vu7i2wJlvq35VQDk558+1AI3DzpvE1KDN7o1lSr6Pa8GSbMu/q5HP09i0wvPUx+Pqovbo38GYh66st//q6ruii7Sag52GeFlOScXT5ABKmNwrgtosluN/bGByOydfOzHCoITc3s6+nrfWy9rs8uj/6LXivobdwpru2cLVs5KjaEKzdEy7mYXDwMoAUYSnKWmXk2RQyUYAdzIYei8Qv6ib59G8XHWIMZHg1MvB/9+36+WzutAWzdRd0djaj2pXZHF/nXRey6ubADxdbDpMVc5DBt9oHa9YI5U9F7JyRNCtjIGJnWqb1eLh59vq5dXow9janMasgHWesg58uzSSr31kTrGJdAAqXH1dS0WpSiGxYDjYk1m5mnbHnHCfoKOXmKGyrrXBvMi8wa+3u57Wu7a6kHWDeHFLj8N4mYGMhEhMVGYAR2mGllpWrm9vY0lMNDxOcnuFsI96ppKGu66ouLCrkpyjqa5o3axtxpF5Yo6xurShzZI9VV95P2KBRE1dAC5DX2RKW4VaYD1RZztLYamQgMu1k4+hcKWsoXGSscrPtpi4r83Nucajlj97r3t+hpt6XVZ2jzOH0EdmkQCKYUhbYVd9cU5xXFaSeWuwl4zHwrubu6bHw7xqj7OvxcTBtYPYzHPZgVO1sKOfiXNtYU6tZj2FV09UVWsAk142NFNejVlUpG5Pn3lbmnpwt6CKqqCbusjRxLV8tLyXh77niLXAurJ60bpltreVR3RxYV9cfXiDW1dhAF9GQFQ0MHZGRkg9PysqNVpAOqN/aMCzqZ2jjGp7d5aomYqjm154j6aMW4h2YZSWqI56ZYd1XmZjblJNWTv4yC4z27WXAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Start working with AI in Software Development&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/f31a700d1e3c53f2859abe258b6ec78d/3c492/how-to-start.png&quot;
        srcset=&quot;/static/f31a700d1e3c53f2859abe258b6ec78d/ff46a/how-to-start.png 325w,
/static/f31a700d1e3c53f2859abe258b6ec78d/a6d36/how-to-start.png 650w,
/static/f31a700d1e3c53f2859abe258b6ec78d/3c492/how-to-start.png 1300w,
/static/f31a700d1e3c53f2859abe258b6ec78d/71c1d/how-to-start.png 1536w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Most engineers we talk to are not skeptical about AI-assisted development. They are interested but stuck. They are waiting for the right project, the right tooling setup, or some form of organizational buy-in before they begin. That moment rarely arrives on its own. The practical alternative is much simpler: run a small, bounded experiment today on code you already know. Not a week-long initiative. An hour.&lt;/p&gt;
&lt;p&gt;This post gives you a mental model for working with AI, the single skill that matters most, clear guidance on when to slow down, and a concrete first task you can try before your next meeting.&lt;/p&gt;
&lt;h2 id=&quot;stop-waiting-start-experimenting&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stop-waiting-start-experimenting&quot; aria-label=&quot;stop waiting start experimenting permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Stop Waiting, Start Experimenting&lt;/h2&gt;
&lt;p&gt;Two weeks of focused, hands-on work will teach you more about where AI helps in your context than a year of reading blog posts (not this one! ;)). The trap is treating AI adoption as a big decision that requires preparation. It is not. It is a series of small experiments that build intuition. The prompts in this post work with any major AI assistant - ChatGPT, Claude, Copilot, Cursor, or whatever your organization permits. If corporate policy restricts tool access, that is a real blocker worth escalating, but it is not a reason to delay learning the underlying skill.&lt;/p&gt;
&lt;p&gt;The key principle: &lt;strong&gt;start with familiar territory&lt;/strong&gt;. Pick a codebase, a module, or a problem you already understand well. When you know the ground truth, you can evaluate AI output accurately. You will spot where it oversimplifies, where it misses domain nuance, and where it nails something that would have taken you twenty minutes to write. That calibration is the foundation for trusting AI on unfamiliar work later.&lt;/p&gt;
&lt;p&gt;If you wait for the perfect project, you are optimizing for a scenario that does not exist. If you start with something you know, you are building judgment that transfers to everything you do next.&lt;/p&gt;
&lt;h2 id=&quot;ai-is-an-amplifier-not-a-replacement&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ai-is-an-amplifier-not-a-replacement&quot; aria-label=&quot;ai is an amplifier not a replacement permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AI Is an Amplifier, Not a Replacement&lt;/h2&gt;
&lt;p&gt;The most useful mental model we have found: AI is a &lt;strong&gt;force multiplier&lt;/strong&gt;. It does not generate quality on its own. It amplifies whatever direction you point it in - including the wrong one.&lt;/p&gt;
&lt;p&gt;This is the part most productivity discussions get wrong. The real gains from AI-assisted development depend heavily on the experience and breadth of knowledge you bring to the session. An experienced engineer who understands the problem space, knows the codebase, and has clear intent will get dramatically more value from AI than someone who is guessing at requirements and hoping the model fills in the gaps. The difference is not a fixed number. It is directional.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It does not matter if you are the fastest runner in the world if you run in the wrong direction.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;AI handles the mechanical parts of development well: boilerplate, syntax, repetitive refactoring, first-draft documentation. What it cannot do is understand the actual problem, design the right solution, or judge whether the output aligns with business goals. Those stay with you. The shift is not from engineer to spectator. It is from bricklayer to architect - you direct execution and validate results instead of doing all the mechanical work yourself.&lt;/p&gt;
&lt;p&gt;Here is what that amplification effect looks like in practice:&lt;/p&gt;
&lt;p&gt;&lt;svg id=&quot;mermaid-0&quot; width=&quot;100%&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; style=&quot;max-width: 500px;&quot; viewBox=&quot;0 0 500 500&quot; role=&quot;graphics-document document&quot; aria-roledescription=&quot;quadrantChart&quot;&gt;&lt;style&gt;#mermaid-0{font-family:arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-0 .error-icon{fill:#552222;}#mermaid-0 .error-text{fill:#552222;stroke:#552222;}#mermaid-0 .edge-thickness-normal{stroke-width:1px;}#mermaid-0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-0 .marker{fill:#333333;stroke:#333333;}#mermaid-0 .marker.cross{stroke:#333333;}#mermaid-0 svg{font-family:arial,sans-serif;font-size:16px;}#mermaid-0 p{margin:0;}#mermaid-0 .node .neo-node{stroke:#9370DB;}#mermaid-0 [data-look=&quot;neo&quot;].node rect,#mermaid-0 [data-look=&quot;neo&quot;].cluster rect,#mermaid-0 [data-look=&quot;neo&quot;].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].swimlane.cluster rect{filter:none;}#mermaid-0 [data-look=&quot;neo&quot;].node path{stroke:#9370DB;stroke-width:1px;}#mermaid-0 [data-look=&quot;neo&quot;].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].node .neo-line path{stroke:#9370DB;filter:none;}#mermaid-0 [data-look=&quot;neo&quot;].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].node circle .state-start{fill:#000000;}#mermaid-0 [data-look=&quot;neo&quot;].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 :root{--mermaid-font-family:arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;/g&gt;&lt;g class=&quot;main&quot;&gt;&lt;g class=&quot;quadrants&quot;&gt;&lt;g class=&quot;quadrant&quot;&gt;&lt;rect x=&quot;263&quot; y=&quot;45&quot; width=&quot;232&quot; height=&quot;212&quot; fill=&quot;#ECECFF&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;0&quot; y=&quot;0&quot; fill=&quot;#131300&quot; font-size=&quot;16&quot; dominant-baseline=&quot;hanging&quot; text-anchor=&quot;middle&quot; transform=&quot;translate(379, 50) rotate(0)&quot;&gt;Fast, high-quality delivery&lt;/text&gt;&lt;/g&gt;&lt;g class=&quot;quadrant&quot;&gt;&lt;rect x=&quot;31&quot; y=&quot;45&quot; width=&quot;232&quot; height=&quot;212&quot; fill=&quot;#f1f1ff&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;0&quot; y=&quot;0&quot; fill=&quot;#0e0e00&quot; font-size=&quot;16&quot; dominant-baseline=&quot;hanging&quot; text-anchor=&quot;middle&quot; transform=&quot;translate(147, 50) rotate(0)&quot;&gt;Faster bugs at scale&lt;/text&gt;&lt;/g&gt;&lt;g class=&quot;quadrant&quot;&gt;&lt;rect x=&quot;31&quot; y=&quot;257&quot; width=&quot;232&quot; height=&quot;212&quot; fill=&quot;#f6f6ff&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;0&quot; y=&quot;0&quot; fill=&quot;#090900&quot; font-size=&quot;16&quot; dominant-baseline=&quot;hanging&quot; text-anchor=&quot;middle&quot; transform=&quot;translate(147, 262) rotate(0)&quot;&gt;Slow, fragile output&lt;/text&gt;&lt;/g&gt;&lt;g class=&quot;quadrant&quot;&gt;&lt;rect x=&quot;263&quot; y=&quot;257&quot; width=&quot;232&quot; height=&quot;212&quot; fill=&quot;#fbfbff&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;0&quot; y=&quot;0&quot; fill=&quot;#040400&quot; font-size=&quot;16&quot; dominant-baseline=&quot;hanging&quot; text-anchor=&quot;middle&quot; transform=&quot;translate(379, 262) rotate(0)&quot;&gt;Solid but slower delivery&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;border&quot;&gt;&lt;line x1=&quot;30&quot; y1=&quot;45&quot; x2=&quot;496&quot; y2=&quot;45&quot; style=&quot;stroke: rgb(199, 199, 241); stroke-width: 2;&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;495&quot; y1=&quot;46&quot; x2=&quot;495&quot; y2=&quot;468&quot; style=&quot;stroke: rgb(199, 199, 241); stroke-width: 2;&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;30&quot; y1=&quot;469&quot; x2=&quot;496&quot; y2=&quot;469&quot; style=&quot;stroke: rgb(199, 199, 241); stroke-width: 2;&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;31&quot; y1=&quot;46&quot; x2=&quot;31&quot; y2=&quot;468&quot; style=&quot;stroke: rgb(199, 199, 241); stroke-width: 2;&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;263&quot; y1=&quot;46&quot; x2=&quot;263&quot; y2=&quot;468&quot; style=&quot;stroke: rgb(199, 199, 241); stroke-width: 1;&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;32&quot; y1=&quot;257&quot; x2=&quot;494&quot; y2=&quot;257&quot; style=&quot;stroke: rgb(199, 199, 241); stroke-width: 1;&quot;&gt;&lt;/line&gt;&lt;/g&gt;&lt;g class=&quot;data-points&quot;&gt;&lt;g class=&quot;data-point&quot;&gt;&lt;circle cx=&quot;123.8&quot; cy=&quot;384.20000000000005&quot; r=&quot;5&quot; fill=&quot;hsl(240, 100%, NaN%)&quot; stroke=&quot;hsl(240, 100%, NaN%)&quot; stroke-width=&quot;0px&quot;&gt;&lt;/circle&gt;&lt;text x=&quot;0&quot; y=&quot;0&quot; fill=&quot;#131300&quot; font-size=&quot;12&quot; dominant-baseline=&quot;hanging&quot; text-anchor=&quot;middle&quot; transform=&quot;translate(123.8, 389.20000000000005) rotate(0)&quot;&gt;Weak fundamentals alone&lt;/text&gt;&lt;/g&gt;&lt;g class=&quot;data-point&quot;&gt;&lt;circle cx=&quot;402.2&quot; cy=&quot;384.20000000000005&quot; r=&quot;5&quot; fill=&quot;hsl(240, 100%, NaN%)&quot; stroke=&quot;hsl(240, 100%, NaN%)&quot; stroke-width=&quot;0px&quot;&gt;&lt;/circle&gt;&lt;text x=&quot;0&quot; y=&quot;0&quot; fill=&quot;#131300&quot; font-size=&quot;12&quot; dominant-baseline=&quot;hanging&quot; text-anchor=&quot;middle&quot; transform=&quot;translate(402.2, 389.20000000000005) rotate(0)&quot;&gt;Strong fundamentals alone&lt;/text&gt;&lt;/g&gt;&lt;g class=&quot;data-point&quot;&gt;&lt;circle cx=&quot;123.8&quot; cy=&quot;129.79999999999998&quot; r=&quot;5&quot; fill=&quot;hsl(240, 100%, NaN%)&quot; stroke=&quot;hsl(240, 100%, NaN%)&quot; stroke-width=&quot;0px&quot;&gt;&lt;/circle&gt;&lt;text x=&quot;0&quot; y=&quot;0&quot; fill=&quot;#131300&quot; font-size=&quot;12&quot; dominant-baseline=&quot;hanging&quot; text-anchor=&quot;middle&quot; transform=&quot;translate(123.8, 134.79999999999998) rotate(0)&quot;&gt;Weak fundamentals + AI&lt;/text&gt;&lt;/g&gt;&lt;g class=&quot;data-point&quot;&gt;&lt;circle cx=&quot;411.47999999999996&quot; cy=&quot;108.60000000000001&quot; r=&quot;5&quot; fill=&quot;hsl(240, 100%, NaN%)&quot; stroke=&quot;hsl(240, 100%, NaN%)&quot; stroke-width=&quot;0px&quot;&gt;&lt;/circle&gt;&lt;text x=&quot;0&quot; y=&quot;0&quot; fill=&quot;#131300&quot; font-size=&quot;12&quot; dominant-baseline=&quot;hanging&quot; text-anchor=&quot;middle&quot; transform=&quot;translate(411.47999999999996, 113.60000000000001) rotate(0)&quot;&gt;Strong fundamentals + AI&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;labels&quot;&gt;&lt;g class=&quot;label&quot;&gt;&lt;text x=&quot;0&quot; y=&quot;0&quot; fill=&quot;#131300&quot; font-size=&quot;16&quot; dominant-baseline=&quot;hanging&quot; text-anchor=&quot;middle&quot; transform=&quot;translate(147, 479) rotate(0)&quot;&gt;Weak Engineering Practices&lt;/text&gt;&lt;/g&gt;&lt;g class=&quot;label&quot;&gt;&lt;text x=&quot;0&quot; y=&quot;0&quot; fill=&quot;#131300&quot; font-size=&quot;16&quot; dominant-baseline=&quot;hanging&quot; text-anchor=&quot;middle&quot; transform=&quot;translate(379, 479) rotate(0)&quot;&gt;Strong Engineering Practices&lt;/text&gt;&lt;/g&gt;&lt;g class=&quot;label&quot;&gt;&lt;text x=&quot;0&quot; y=&quot;0&quot; fill=&quot;#131300&quot; font-size=&quot;16&quot; dominant-baseline=&quot;hanging&quot; text-anchor=&quot;middle&quot; transform=&quot;translate(5, 363) rotate(-90)&quot;&gt;Lower Output Quality&lt;/text&gt;&lt;/g&gt;&lt;g class=&quot;label&quot;&gt;&lt;text x=&quot;0&quot; y=&quot;0&quot; fill=&quot;#131300&quot; font-size=&quot;16&quot; dominant-baseline=&quot;hanging&quot; text-anchor=&quot;middle&quot; transform=&quot;translate(5, 151) rotate(-90)&quot;&gt;Higher Output Quality&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;title&quot;&gt;&lt;text x=&quot;0&quot; y=&quot;0&quot; fill=&quot;#131300&quot; font-size=&quot;20&quot; dominant-baseline=&quot;hanging&quot; text-anchor=&quot;middle&quot; transform=&quot;translate(250, 10) rotate(0)&quot;&gt;AI Amplification Effect&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/p&gt;
&lt;p&gt;Strong engineering practices combined with AI produce fast, high-quality delivery. Weak foundations combined with AI produce bugs faster and at larger scale. The AI does not judge what it amplifies. Your practices, your domain knowledge, and your engineering judgment determine the outcome - AI just makes it happen faster.&lt;/p&gt;
&lt;h2 id=&quot;context-engineering-the-skill-that-actually-matters&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#context-engineering-the-skill-that-actually-matters&quot; aria-label=&quot;context engineering the skill that actually matters permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Context Engineering: The Skill That Actually Matters&lt;/h2&gt;
&lt;p&gt;If there is one skill that separates effective AI-assisted development from frustrating prompt-retry loops, it is &lt;strong&gt;context engineering&lt;/strong&gt; - structuring the input you give to AI so the output is actually useful on the first pass.&lt;/p&gt;
&lt;p&gt;Vague prompts produce generic output. Precise prompts, loaded with relevant context, produce drafts worth building on. The difference is not about cleverness or prompt tricks. It is about specificity.&lt;/p&gt;
&lt;p&gt;Compare these two prompts for the same task:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vague:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Write tests for the user service.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Precise:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Write unit tests for the UserService class in /src/services/user-service.ts.
It uses the Repository pattern with a PostgreSQL-backed UserRepository.
Cover the createUser and deactivateUser methods, including the case where
deactivateUser is called on an already-inactive user. Use Jest. Follow the
existing test patterns in /src/services/__tests__/.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The second prompt is not clever - it is specific. It names the file, the patterns in use, the edge cases that matter, and the conventions to follow. That specificity is what makes the output useful on the first pass rather than the third.&lt;/p&gt;
&lt;p&gt;This is why we treat &lt;a href=&quot;https://handsonarchitects.com/blog/2026/ai-toolset-for-software-architect-2026q1/#prompt-engineering-as-a-metacategory&quot;&gt;prompting as an engineering discipline&lt;/a&gt;. It rewards the same rigor you apply to code: clear intent, relevant context, explicit constraints, and iterative refinement. The engineers who get the most from AI are not the ones with the fanciest tools. They are the ones who invest a few extra minutes describing what they actually need.&lt;/p&gt;
&lt;h2 id=&quot;when-to-slow-down&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#when-to-slow-down&quot; aria-label=&quot;when to slow down permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;When to Slow Down&lt;/h2&gt;
&lt;p&gt;AI-generated code can introduce subtle bugs, miss domain nuances, reference deprecated APIs, or misread business logic - all with total confidence. You are the validation layer. Human-in-the-loop is not a suggestion; it is the operating model.&lt;/p&gt;
&lt;p&gt;Three categories consistently require tighter oversight:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Security-critical code.&lt;/strong&gt; Authentication flows, authorization logic, input validation, secrets handling. These areas require human audit regardless of how clean the generated output looks. AI cannot assess the risk implications of what it writes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Novel architecture and problem framing.&lt;/strong&gt; AI is fluent in known patterns. When you face a genuinely new problem or design a system with unusual constraints, the AI will confidently anchor on the closest familiar pattern - which may not fit. Use AI to surface options, but own the design decisions yourself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Learning fundamentals.&lt;/strong&gt; If you are still building core skills in a language or framework, heavy reliance on code generation creates dependency rather than understanding. Use AI to explain, not to replace your thinking. A simple test: if you cannot walk through generated code line by line and explain what it does, you do not yet own it.&lt;/p&gt;
&lt;p&gt;Beyond these categories, watch for &lt;strong&gt;diminishing returns&lt;/strong&gt;. When you have revised the same prompt three or four times and the output keeps missing the mark, stop. Reframe the problem manually, or write the code yourself. Continued iteration rarely recovers a session that has lost direction. At this point we recommend taking a break and returning with fresh eyes later. There is good chance you were tackling problem from the wrong angle, and more prompting is just reinforcing that.&lt;/p&gt;
&lt;p&gt;A practical heuristic for calibrating oversight: &lt;strong&gt;the harder it is to reverse a change, the stricter your review should be&lt;/strong&gt;. A draft document is easy to discard. A refactoring committed to main and deployed is not.&lt;/p&gt;
&lt;h2 id=&quot;your-first-ai-experiment-pick-one&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#your-first-ai-experiment-pick-one&quot; aria-label=&quot;your first ai experiment pick one permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Your First AI Experiment (Pick One)&lt;/h2&gt;
&lt;p&gt;The tasks below are designed for trust-building, not maximum ambition. Each one takes under an hour and uses a problem you already know. Starting with familiar territory gives you ground truth to evaluate AI output - you will catch mistakes you could not catch in unfamiliar code.&lt;/p&gt;
&lt;p&gt;Pick the one that matches your role and try it today.&lt;/p&gt;
&lt;h3 id=&quot;for-engineers-explore-a-codebase-you-already-know&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#for-engineers-explore-a-codebase-you-already-know&quot; aria-label=&quot;for engineers explore a codebase you already know permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;For Engineers: Explore a Codebase You Already Know&lt;/h3&gt;
&lt;p&gt;Pick a module you understand well and ask AI to explain it as if onboarding a new team member.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;I am going to share a module from our codebase. You are a senior software
engineer explaining this code to a new team member who has never seen it.
Assume they are technically proficient but do not know our codebase or domain.
Please explain:
1. The overall responsibility of this module
2. The key components and how they relate to each other
3. Any design patterns in use
4. Anything that looks unusual or potentially problematic

[paste the module code here]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;What to expect:&lt;/strong&gt; A structured explanation you can check against what you know. Where does the AI get it right? Where does it oversimplify or miss context? This calibration tells you what level of trust to extend in future sessions.&lt;/p&gt;
&lt;h3 id=&quot;for-tech-leads-break-a-feature-into-backlog-items&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#for-tech-leads-break-a-feature-into-backlog-items&quot; aria-label=&quot;for tech leads break a feature into backlog items permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;For Tech Leads: Break a Feature into Backlog Items&lt;/h3&gt;
&lt;p&gt;Take a loosely defined feature your team is about to start and ask AI to decompose it.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Break down the following feature into backlog items for my team.

Feature: [describe in 2-3 sentences]
Tech stack: [list relevant technologies]
Key constraints: [deadlines, dependencies, compliance requirements]

For each item include: a clear title, short description, acceptance criteria,
dependencies on other items, and open questions.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;What to expect:&lt;/strong&gt; A structured first draft that covers the main implementation tasks and integration points. You will need to reorder, merge, or split items - the value is in surfacing the decomposition quickly, not accepting it wholesale. Once you have prompts that consistently produce useful output, save them - a shared prompt library is one of the fastest ways to scale AI-assisted practices across a team.&lt;/p&gt;
&lt;h3 id=&quot;for-architects-brainstorm-architectural-options&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#for-architects-brainstorm-architectural-options&quot; aria-label=&quot;for architects brainstorm architectural options permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;For Architects: Brainstorm Architectural Options&lt;/h3&gt;
&lt;p&gt;Pick a design decision you are currently weighing and ask AI to surface alternatives.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;I am designing a solution for the following problem:

Problem: [describe clearly]
Current system context: [relevant services, data flows, constraints]
Key requirements: [non-negotiables]

Suggest 3-4 architectural approaches. For each: describe the approach,
list main advantages, list main disadvantages or risks, and note
assumptions it depends on.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;What to expect:&lt;/strong&gt; A set of options with trade-offs. AI is good at recalling and articulating known patterns - expect solid coverage of conventional approaches. What it will not give you is judgment about which option fits your organizational or operational constraints. Use the output as a menu of options, not a recommendation. Once you are comfortable with brainstorming, try &lt;a href=&quot;https://handsonarchitects.com/blog/2025/using-generative-ai-as-architect-buddy-for-adrs/&quot;&gt;using AI as an architect buddy for ADRs&lt;/a&gt; - it is a natural next step.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Leave your favorite approaches out of the initial prompt to avoid anchoring the model. Add them in a follow-up to compare against the options it generated independently.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;start-today-iterate-tomorrow&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#start-today-iterate-tomorrow&quot; aria-label=&quot;start today iterate tomorrow permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Start Today, Iterate Tomorrow&lt;/h2&gt;
&lt;p&gt;The goal is not one perfect prompt. It is building a habit of AI-assisted work that consistently produces better outcomes than working without it. That habit starts with a single bounded experiment.&lt;/p&gt;
&lt;p&gt;Here is your next step: pick one of the tasks above, set a timer for one hour, and try it on code or a problem you already know. Pay attention to where the AI is helpful and where it falls short. That observation - not any blog post - is what builds the judgment you need for everything that comes next.&lt;/p&gt;
&lt;p&gt;The engineers who start experimenting now will have a significant advantage over those still waiting for the right moment.&lt;/p&gt;
&lt;p&gt;The right moment is today.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[AI Toolset for Software Architects (Q1 2026)]]></title><description><![CDATA[Artificial intelligence is now embedded in day-to-day architecture work. The question is no longer which AI tool to adopt - it is how to…]]></description><link>https://handsonarchitects.com/blog/2026/ai-toolset-for-software-architect-2026q1/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2026/ai-toolset-for-software-architect-2026q1/</guid><pubDate>Thu, 26 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/0f01bb8cbccc4d2fb23aa2220da3dd7c/71c1d/architect-ai-toolset.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAADJElEQVR42gEZA+b8AP389/78+Pz9+/b28+vm4vz7+f369dTa2a7G0NXe3/369f79+f7+/fHy7/f39fz8+v79+v389/38+P389wD9+vb///+xo5x7WEpgPS7FurGpyNtPmcZ8m5A/k8ipwtL///mChYRXUEphWlODhYP6+PT+/Pj9+/f9/PgA////xcC8aD4sr3dSjGRIwbOmhK/MO4OzYImZPYvGoLzR/fr2cWNY0JJjr4RkFRcXx8jG/////Pr3/fz4AP///7uwqmo5IcShhayNc8S1qOLh3tfSzJ+joYGQkLq8tf///7qzrZ+IdbmwpYJaPdDBt/////z69/38+QD+/Pn7/PqskX2RZEuLVzqYe2r6/v7l4NZleXZUnqpTYFLj4NjX2NigeFiYak2ld1SYpare5OX+/Pn8/PkA/v//7+LZvYBaaS0QgE0yh1c98uni+vz7oKimUVdOXmRekaKsy87Ou5+JjF1EaV9bNISxTZO51N7h///9APb+/+CNWdVGAKtbJuWkccBfHr95S/Py7f7//9XCt6qKdlhXWVh0iJuhpVhtdj15m0eGqDuJs4eqvP//+gD2+vnMeUSxVB2/gmGwXzmwRBSBUz2AV0WbaV+RXlezjYW3vsBBaIJlY2G0d1Bjip1Ek7xFkblTgp728u0A/f/9sIZvgCweejQtbSskiCkfjDQqky4hpCkXmyENoDko+fHu2ebroaanyLSmU3eMNnuiPX2jK2qUztbZAP///M6ulpkpD4RHQYeGga44JIxQSKBWSqMqFoQsH30qHKZtZN/Fvv37+f///5Kwvyt1oDqIth9snKKuswD/+/bLooG0OwlrMi+WpJ+gj4RIU1pdY2lPNjZBLy6ANCijKxmbLBrZwrr///nKyMMlTmYvX3gTJzJ3dHAA/+3edICHFiQxijAgolJDlXNodklEkz8zkT4xU1RUqzooujMdqTAd07qu//zvsqqiExcaJyYlCxAUUVJSAOzYx0tebAEjPY4tH7MzH4YhEpYlFqAoF5wqG5MsH5osHpMoHoUoIMixpv3t3oF8eQsVHX96dRcdITs8POWGy4rbsnC2AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Overview of AI tools and categories used by software architects in Q1 2026, covering research, brainstorming, planning, coding, and quality assurance&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/0f01bb8cbccc4d2fb23aa2220da3dd7c/3c492/architect-ai-toolset.png&quot;
        srcset=&quot;/static/0f01bb8cbccc4d2fb23aa2220da3dd7c/ff46a/architect-ai-toolset.png 325w,
/static/0f01bb8cbccc4d2fb23aa2220da3dd7c/a6d36/architect-ai-toolset.png 650w,
/static/0f01bb8cbccc4d2fb23aa2220da3dd7c/3c492/architect-ai-toolset.png 1300w,
/static/0f01bb8cbccc4d2fb23aa2220da3dd7c/71c1d/architect-ai-toolset.png 1536w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Artificial intelligence is now embedded in day-to-day architecture work. The question is no longer &lt;em&gt;which&lt;/em&gt; AI tool to adopt - it is &lt;strong&gt;how to curate, evolve, and integrate a continuously updated toolset&lt;/strong&gt; across the full software development lifecycle. Since our &lt;a href=&quot;https://handsonarchitects.com/blog/2025/ai-toolset-for-software-architect-2025q3/&quot;&gt;Q3 2025 edition&lt;/a&gt;, the landscape has shifted in meaningful ways: we added two categories (&lt;strong&gt;Planning &amp;#x26; Project Management&lt;/strong&gt; and &lt;strong&gt;Quality Assurance / Continuous Integration&lt;/strong&gt;), one option we expected to lead (OpenAI Codex) underdelivered in practice, and our prompting approach matured into a discipline that cuts across everything we do.&lt;/p&gt;
&lt;p&gt;This post covers the current state of our AI toolset as of February 2026, highlights what changed, and shares the lessons we picked up along the way. If you are new to the series, the Q3 2025 edition covered four categories - Searching &amp;#x26; Learning, Brainstorming &amp;#x26; Ideation, Prompt Engineering, and Agentic Coding &amp;#x26; Prototyping. Here is what shifted since then.&lt;/p&gt;
&lt;h2 id=&quot;how-the-architects-role-continues-to-evolve-with-ai&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-the-architects-role-continues-to-evolve-with-ai&quot; aria-label=&quot;how the architects role continues to evolve with ai permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How the Architect&apos;s Role Continues to Evolve with AI&lt;/h2&gt;
&lt;p&gt;Four principles guide how we build and maintain our toolset:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Build a complementary toolset, not one perfect tool.&lt;/strong&gt; No single AI covers everything. Combine tools that cover research, design, planning, coding, and quality - each doing what it does best.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treat your toolset as continuously evolving.&lt;/strong&gt; Adopt an experimentation habit rather than searching for a stable, final configuration. What works today may be outpaced in a quarter - or sometimes within a week. For example, once &lt;a href=&quot;https://agentskills.io/home&quot;&gt;Skills&lt;/a&gt; gained broad attention as a reusable capability standard, MCP (Model Context Protocol - a standard for connecting AI agents to external tools and data sources) usage shifted away from being a local tooling accelerator and toward a clearer role: bridging agents to remote services and live data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leverage AI across all lifecycle stages.&lt;/strong&gt; From early research and design through planning, implementation, and into maintenance - there is no phase where AI cannot accelerate or augment your work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Capture and operationalize shared knowledge.&lt;/strong&gt; Decisions made in the morning should inform work done in the afternoon. Meeting transcription and insight extraction are key parts of this process. Maintaining up-to-date documentation is essential for AI to provide accurate, relevant assistance.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;what-changed-since-q3-2025&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-changed-since-q3-2025&quot; aria-label=&quot;what changed since q3 2025 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What Changed Since Q3 2025&lt;/h2&gt;
&lt;p&gt;The most significant structural change in our toolset is that &lt;strong&gt;Prompt Engineering is no longer its own category&lt;/strong&gt;. It has become a &lt;em&gt;metacategory&lt;/em&gt; - a cross-cutting discipline that shapes how we interact with every other tool. Good prompting is now an integral part of all our AI workflows, not a specialized skill in one workflow stage. (See &lt;a href=&quot;#prompt-engineering-as-a-metacategory&quot;&gt;Prompt Engineering as a Metacategory&lt;/a&gt; below for what this means in practice.)&lt;/p&gt;
&lt;p&gt;Equally significant: we expanded into two new areas that were missing from the previous edition:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Planning &amp;#x26; Project Management&lt;/strong&gt; - we started actively using AI to support backlog grooming, task decomposition, and integration with project management tools.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quality Assurance / Continuous Integration&lt;/strong&gt; - we now use AI-assisted hooks, code review automation, and browser testing tools as part of our CI feedback loops.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The five-category model we use now better reflects where architects actually spend time.&lt;/p&gt;
&lt;h2 id=&quot;ai-tool-categories---q1-2026&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ai-tool-categories---q1-2026&quot; aria-label=&quot;ai tool categories   q1 2026 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AI Tool Categories - Q1 2026&lt;/h2&gt;
&lt;h3 id=&quot;1-searching--learning&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-searching--learning&quot; aria-label=&quot;1 searching  learning permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Searching &amp;#x26; Learning&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.perplexity.ai/&quot;&gt;Perplexity.ai&lt;/a&gt; remains our primary tool for quick search across sources, exploring new technologies, and summarizing recent research. What has changed is &lt;em&gt;how&lt;/em&gt; we use it: increasingly via its API inside more complex, multi-step workflows rather than as a standalone chat interface.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://obsidian.md/&quot;&gt;Obsidian&lt;/a&gt; is where this research ultimately lands, and Markdown is now our default documentation language. That decision is mostly practical: Markdown is the native format we use to communicate with LLMs, and it handles prose, code snippets, and diagram-as-code (for example Mermaid) equally well in one place. In practice, this gives us a better AI interface than wiki-style pages such as Confluence, which are harder to keep structured, portable, and model-friendly at scale. Every research thread ends as a curated Markdown knowledge-base entry with clear explanations, practical analogies, best practices, and verified examples. We then connect this local knowledge base back into AI chats via local MCP/RAG, creating a continuously improving feedback loop between daily work and future AI sessions.&lt;/p&gt;
&lt;p&gt;A typical pattern looks like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use Perplexity API to search and identify relevant sources on a topic.&lt;/li&gt;
&lt;li&gt;Pipe results into &lt;a href=&quot;https://docs.firecrawl.dev/&quot;&gt;Firecrawl&lt;/a&gt; to scrape and extract full content from the most promising pages.&lt;/li&gt;
&lt;li&gt;Summarize and synthesize using an LLM (e.g., Claude) to extract key insights from the aggregated content.&lt;/li&gt;
&lt;li&gt;If the topic is generic enough to be reusable, create a structured Markdown entry in Obsidian with the synthesized insights, examples, and references.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This pipeline makes AI-powered research significantly more thorough and reproducible than ad-hoc searches. It is especially effective when you need to validate architectural assumptions against current documentation, vendor blogs, and community discussions simultaneously.&lt;/p&gt;
&lt;p&gt;Over time, these entries become curated, personalized training content for the team. We loop that context back into future AI sessions so new prompts are grounded in our language, decisions, and mental models rather than generic internet summaries.&lt;/p&gt;
&lt;p&gt;During research, we also ask AI to explain complex problems and architectures visually using Mermaid diagrams. Mermaid works especially well with Markdown-native editors because diagrams are rendered directly from source, without the extra PNG/SVG export step, which keeps them easy to update as ideas evolve. In practice, we use it for multiple diagram types: flowcharts, sequence diagrams, state diagrams, and C4 diagrams (context, container, component, and deployment views). Because these notes live in Git, diagram changes can be reviewed in normal pull requests, where team members comment on specific lines and propose improvements exactly as they do for code.&lt;/p&gt;
&lt;h3 id=&quot;2-brainstorming--ideation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-brainstorming--ideation&quot; aria-label=&quot;2 brainstorming  ideation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Brainstorming &amp;#x26; Ideation&lt;/h3&gt;
&lt;p&gt;Chat interfaces continue to work well for early-stage thinking. We use both &lt;a href=&quot;https://chat.openai.com/&quot;&gt;ChatGPT&lt;/a&gt; and &lt;a href=&quot;https://claude.ai/&quot;&gt;Claude&lt;/a&gt; (the chat interface, not Claude Code) depending on the task - neither has displaced the other in this category. What matters more than the model is the quality of the prompt and the clarity of what you are trying to explore.&lt;/p&gt;
&lt;p&gt;Brainstorming use cases remain largely unchanged: generating design alternatives, stress-testing assumptions, exploring edge cases before committing to a direction, and facilitating design discussions when you want a second (non-human) perspective.&lt;/p&gt;
&lt;p&gt;What has improved is our capture-and-review loop. The difference is concrete. Before: chat logs from design discussions and meeting notes sit in Slack threads or personal notebooks, slowly going stale and disconnected from the codebase. After: AI converts those same transcripts and chat outputs into structured Markdown documentation with Mermaid diagrams that live in the repo, are version-controlled, and stay current as the design evolves. In practice, this means a 30-minute architecture brainstorm now produces a pull request with updated context diagrams and decision rationale - not a Slack message that three people will search for next week and nobody will find.&lt;/p&gt;
&lt;h3 id=&quot;3-planning--project-management&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-planning--project-management&quot; aria-label=&quot;3 planning  project management permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Planning &amp;#x26; Project Management&lt;/h3&gt;
&lt;p&gt;This is a new category in our toolset - and one of the more nuanced additions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For smaller projects&lt;/strong&gt;, we prefer simple Markdown-based backlogs and task lists maintained in the repository. They are lightweight, version-controlled, and work well with AI tools that can read and update them directly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For larger projects involving multiple team members&lt;/strong&gt;, we use the &lt;a href=&quot;https://www.atlassian.com/platform/mcp&quot;&gt;Atlassian MCP&lt;/a&gt; to interact with Jira within Claude Code sessions. However, there is an important practical caveat: the Atlassian MCP is token-heavy and consumes significant context. Our recommendation is to &lt;strong&gt;avoid running it continuously&lt;/strong&gt;. Instead, adopt a sync-work-sync pattern:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sync Jira → Markdown (pull the current state into local files).&lt;/li&gt;
&lt;li&gt;Do the planning and task management work in Markdown.&lt;/li&gt;
&lt;li&gt;Sync Markdown → Jira (push updates back) in a separate session.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This keeps your AI sessions focused and avoids flooding the context window with Jira API overhead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A broader observation&lt;/strong&gt;: Jira is becoming less useful for engineering teams. In practice, it primarily serves project managers who need visibility - but it adds friction for engineers. We are actively exploring lighter alternatives, starting with Markdown-based backlogs in Git repositories, and watching &lt;a href=&quot;https://linear.app/&quot;&gt;Linear&lt;/a&gt; as a potential replacement. Some of the more technical Project Managers already started to use Claude Code to &quot;ask&quot; Markdown-based backlogs for status updates and to provide updates to stakeholders - this is a much more efficient workflow than navigating Jira&apos;s UI for the same purpose.&lt;/p&gt;
&lt;p&gt;In practice, this category now consumes more time than coding in early phases of a project. We use AI to shape clearer roadmaps and produce more detailed milestones and task definitions that are implementation-ready for the team.&lt;/p&gt;
&lt;p&gt;This up-front decomposition is essential for AI-first delivery: when milestones and tasks are explicit, scoped, and well-sequenced, execution accelerates dramatically. We intentionally invest more effort at the beginning to align on the problem and solution space before writing code.&lt;/p&gt;
&lt;h3 id=&quot;4-coding-and-prototyping&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-coding-and-prototyping&quot; aria-label=&quot;4 coding and prototyping permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Coding and Prototyping&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://claude.ai/code&quot;&gt;Claude Code&lt;/a&gt; remains our frontier tool for agentic coding. After a brief experiment with &lt;strong&gt;OpenAI Codex&lt;/strong&gt; and &lt;strong&gt;&lt;a href=&quot;https://www.augmentcode.com/&quot;&gt;Augment Code&lt;/a&gt;&lt;/strong&gt;, we returned to Claude Code - it remains ahead in terms of agentic behavior, context handling, and integration ecosystem.&lt;/p&gt;
&lt;p&gt;Our current focus is on &lt;strong&gt;building skills and &lt;a href=&quot;#agent-or-agentic&quot;&gt;subagent&lt;/a&gt; libraries for specific projects&lt;/strong&gt; and testing how those building blocks scale in larger teams (3+ engineers), with mixed but promising results so far. Rather than relying on general-purpose prompting (or off-the-shelf prompt templates and frameworks), we invest in project-specific, high-quality assets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Skills&lt;/strong&gt;: reusable, task-specific prompt packages (slash commands) that encode domain knowledge, constraints, and preferred workflows for a given project context. Think of them as documented expert procedures that any agent session can load. A concrete example is a Docker image validation skill focused on checking images against container best practices and verifying base-image choices. Skills like this often bundle scripts and validation logic, giving agents stronger guardrails than prompt text alone.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;#agent-or-agentic&quot;&gt;&lt;strong&gt;Subagents&lt;/strong&gt;&lt;/a&gt;: specialized Claude Code agent instances scoped to a focused task - for example, a Domain-Driven Design expert that helps identify bounded contexts, a Code Reviewer tuned to a specific language or framework, or a Trade-off Analyst that evaluates architectural options against defined criteria.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The distinction matters: a skill is a &lt;em&gt;single-purpose, reusable prompt package&lt;/em&gt;, while a subagent is a &lt;em&gt;scoped execution context, that can run in scaled sessions and be orchestrated with other subagents&lt;/em&gt;. Skills are what subagents follow.&lt;/p&gt;
&lt;p&gt;Once the library of skills and subagents is in place, we execute project backlogs using teams of subagents operating with those specific skills. This dramatically improves consistency and reduces the per-task prompting overhead.&lt;/p&gt;
&lt;p&gt;We continue to use a small, curated set of MCPs:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;MCP&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/oraios/serena&quot;&gt;Serena&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Codebase querying - works especially well with larger codebases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/upstash/context7&quot;&gt;Context7&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Documentation browsing during coding sessions, more &lt;a href=&quot;https://handsonarchitects.com/blog/2026/what-makes-mcp-server-successful/&quot;&gt;here&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking&quot;&gt;Sequential Thinking&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Structured planning and multi-step reasoning&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Keeping the MCP list small and intentional is a deliberate choice. Each MCP adds context overhead and potential noise - more is not always better (we are aware of Claude&apos;s MCP auto-discovery feature, but managing context explicitly has proven more reliable).&lt;/p&gt;
&lt;p&gt;We also maintain a few cross-project skills that multiple subagents share, such as (but not limited to):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Documentation structure&lt;/strong&gt;: keeps commercial and open-source documentation aligned to a shared, easy-to-navigate structure, with explicit documentation-map files that make content simple for both humans and LLMs to discover and traverse.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation style guide&lt;/strong&gt;: enforces a consistent writing style inspired by the &lt;a href=&quot;https://learn.microsoft.com/en-us/style-guide/welcome/&quot;&gt;Microsoft Writing Style Guide&lt;/a&gt;, implemented as a practical skill split into categories with English examples.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mermaid diagram skill&lt;/strong&gt;: translates natural-language architecture and ADR ideas into the most appropriate Mermaid diagram type and applies consistent visual conventions (shapes, colors, and notation) across repositories.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;5-quality-assurance--continuous-integration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-quality-assurance--continuous-integration&quot; aria-label=&quot;5 quality assurance  continuous integration permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. Quality Assurance / Continuous Integration&lt;/h3&gt;
&lt;p&gt;This is the other new category, and it is one of the most promising areas for architects who want to influence engineering standards at scale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Claude Code Hooks&lt;/strong&gt; are our first &quot;tool&quot; here. &lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/hooks&quot;&gt;Hooks&lt;/a&gt; are lifecycle event handlers in Claude Code that can execute deterministic checks at specific points in the agent workflow. A practical example: attach a script to the &lt;code class=&quot;language-text&quot;&gt;Stop&lt;/code&gt; event (or &lt;code class=&quot;language-text&quot;&gt;SubagentStop&lt;/code&gt; if you are working with subagent orchestration) that automatically runs tests and linters after each agent completes its task. This turns AI-generated code into code that is continuously validated - not just generated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitHub Copilot for code review&lt;/strong&gt; has improved noticeably. AI-assisted code review now covers basic quality checks reliably, and we are increasingly comfortable using it as a &lt;em&gt;first pass&lt;/em&gt; before human review - particularly for generated code, configuration files, or changes that are high-volume but lower-risk. We still keep humans in the loop for anything critical or architecturally significant.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/ChromeDevTools/chrome-devtools-mcp&quot;&gt;Chrome DevTools MCP&lt;/a&gt;&lt;/strong&gt; rounds out this category. It provides a simple but effective way to give AI visibility into web application behavior - inspecting the DOM, checking network requests, and validating rendering - without leaving the agent workflow. For web-focused projects, this closes a gap that previously required manual browser inspection.&lt;/p&gt;
&lt;p&gt;We are exploring how to use AI to automatically validate whether code stays consistent with architectural decisions and documentation. Today we do this periodically as part of our QA process, but the goal is tighter integration - running architectural consistency checks directly in CI pipelines so that drift is caught early, not during a quarterly review. This connects directly to principle #4: if architectural decisions and documentation are kept up to date and in AI-friendly formats, automated validation becomes practical rather than aspirational. Early experiments are promising, though the challenge remains defining &quot;consistency&quot; precisely enough for an AI check to be actionable without generating noise.&lt;/p&gt;
&lt;h2 id=&quot;prompt-engineering-as-a-metacategory&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#prompt-engineering-as-a-metacategory&quot; aria-label=&quot;prompt engineering as a metacategory permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Prompt Engineering as a Metacategory&lt;/h2&gt;
&lt;p&gt;In Q3 2025, Prompt Engineering had its own category. It no longer does - not because it matters less, but because it matters &lt;em&gt;everywhere&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Good prompting is now the backbone of how we interact with every tool in the list. The principles that emerged from dedicated prompt engineering practice - clear context, well-scoped tasks, explicit constraints, iterative refinement - apply equally to Perplexity searches, Jira syncs, Claude Code subagent configurations, and QA hooks.&lt;/p&gt;
&lt;p&gt;At the same time, this is not a rigid &quot;ways of working&quot; template. Prompt engineering is iterative, dynamic, and continuously adjusted as project context changes. The process is rarely linear or fully repeatable; it is a creative collaboration where human judgment remains essential.&lt;/p&gt;
&lt;p&gt;Treating prompting as a metacategory is also a signal to teams: it is an engineering discipline, not a soft skill. It deserves the same investment as writing good tests or designing good APIs.&lt;/p&gt;
&lt;h2 id=&quot;to-explore&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#to-explore&quot; aria-label=&quot;to explore permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;To Explore&lt;/h2&gt;
&lt;p&gt;The toolset never stands still. Here is what is on our radar for the coming quarter:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://linear.app/&quot;&gt;Linear&lt;/a&gt;&lt;/strong&gt; - piloting as a lighter replacement for Jira, with a developer experience that better fits engineering-first teams.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://search.brave.com/&quot;&gt;Brave Search&lt;/a&gt;&lt;/strong&gt; - evaluating as an alternative or complement to Perplexity for searching and learning, particularly for privacy-sensitive research contexts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Be0ceKN81S8&quot;&gt;Playwright CLI with AI&lt;/a&gt;&lt;/strong&gt; - exploring AI-assisted testing capabilities through Playwright&apos;s CLI interface.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.github.io/gh-aw/&quot;&gt;GitHub Agentic Workflows&lt;/a&gt;&lt;/strong&gt; (preview) - build GitHub Workflows with Markdown rather than YAML only. This is a promising direction to use CI in more ways.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;our-current-thoughts-on-working-with-ai-tools&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#our-current-thoughts-on-working-with-ai-tools&quot; aria-label=&quot;our current thoughts on working with ai tools permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Our Current Thoughts on Working with AI Tools&lt;/h2&gt;
&lt;p&gt;After another quarter of hands-on work, four things stand out as field notes from the trenches:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI accelerates thinking - it does not replace it.&lt;/strong&gt; AI is a force multiplier for engineering judgment, not a substitute. The more architectural maturity you bring to AI collaboration, the better your outcomes. Architects who lean on AI to skip the thinking tend to get mediocre results faster. (For more on the foundational skills that make this work, see our post on &lt;a href=&quot;https://handsonarchitects.com/blog/2025/staff-engineer-toolkit/&quot;&gt;the Staff Engineer Toolkit&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prompting is an engineering discipline.&lt;/strong&gt; Own the context. We consistently observe a significant difference between sessions that have too little context (under-informed, generic output) and sessions that have too much context (noisy, unfocused output). There is no silver bullet - but the habit of &lt;em&gt;researching and documenting context close to the task&lt;/em&gt; before starting an AI session pays dividends every time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI amplifies both good and bad practices.&lt;/strong&gt; If your processes are sloppy, AI will make them sloppier at scale. If your architectural thinking is sharp, AI will amplify it. This is perhaps the strongest argument for investing in learning and foundational understanding: in a landscape where implementation details can be delegated to AI, the competitive advantage lies in understanding the &lt;em&gt;why&lt;/em&gt; behind those details - well enough to guide, review, and correct the AI&apos;s output.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Automated information flow and collection is key.&lt;/strong&gt; Whether it&apos;s research, documentation, or test case generation, the quality of AI output depends critically on the information it has access to. Automated (or semi-automated) collection of up-to-date information from multiple sources (meeting transcripts/notes, &lt;a href=&quot;https://handsonarchitects.com/blog/2025/using-generative-ai-as-architect-buddy-for-adrs/&quot;&gt;Architecture Decision Records&lt;/a&gt;, Confluence pages, Roadmaps, changelogs, etc.) and transforming it into consistent AI-friendly formats is essential.&lt;/p&gt;
&lt;h2 id=&quot;closing-principles&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#closing-principles&quot; aria-label=&quot;closing principles permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Closing Principles&lt;/h2&gt;
&lt;p&gt;There is still no final or perfect AI toolset for software architects. Quarter by quarter, the practice comes down to four durable commitments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Continuous evolution&lt;/strong&gt;: adopt, test, and retire tools as needs change. One quarter&apos;s frontier tool may be the next quarter&apos;s baseline - OpenAI Codex being a fresh example from this edition.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Balanced coverage&lt;/strong&gt;: ensure the toolset spans research, ideation, planning, coding, and quality. Gaps in any stage create bottlenecks that slow the whole team.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Architect mindset first&lt;/strong&gt;: tools augment judgment; they do not replace it. The most valuable thing you bring to any AI collaboration is clear thinking about what you actually need and why.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shared knowledge as infrastructure&lt;/strong&gt;: capture decisions, research, and context in formats that both humans and AI can use. The quality of AI output depends on the quality of information it has access to.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The architects who thrive in this environment are not those who adopt the most tools - they are the ones who build the habit of thoughtful experimentation and honest evaluation, one quarter at a time.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;The following is a terminology note for readers less familiar with how &quot;agent&quot; and &quot;agentic&quot; are used in this post and in the broader industry.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;agent-or-agentic&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#agent-or-agentic&quot; aria-label=&quot;agent or agentic permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Agent or Agentic&lt;/h3&gt;
&lt;p&gt;&quot;Agent&quot; and &quot;agentic&quot; are among the most overloaded terms in the AI space - right alongside &quot;artifact&quot; or &quot;service.&quot; They mean very different things depending on context, and the lack of consensus creates real confusion. Here is how the ambiguity plays out in practice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&quot;Agent&quot; as a runtime orchestrator&lt;/strong&gt; - tools like Claude Code that autonomously plan, execute, and iterate on multi-step tasks. This is the strongest sense of the word: an agent that reasons about what to do next and acts on it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&quot;Agent&quot; as a scoped prompt package&lt;/strong&gt; - what many teams call a &quot;code review agent&quot; or &quot;testing agent&quot; is often just a well-crafted prompt with specific instructions, not an autonomous system. In our toolset, we call these &lt;em&gt;subagents&lt;/em&gt; to avoid the confusion.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&quot;Agent&quot; as a single instruction&lt;/strong&gt; - in some contexts, even &quot;write tests for this code&quot; qualifies as an &quot;agent,&quot; stretching the term to near-meaninglessness.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&quot;Agentic&quot; as a behavioral property&lt;/strong&gt; - describes a workflow or tool that exhibits autonomy, tool use, and multi-step reasoning, as opposed to a single request-response exchange. A coding session where the AI reads files, runs tests, and iterates on failures is &lt;em&gt;agentic&lt;/em&gt;; a one-shot code generation prompt is not.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this post, when we say &lt;strong&gt;subagent&lt;/strong&gt; we mean a Claude Code agent instance scoped to a focused task and orchestrated as part of a larger workflow. When we say &lt;strong&gt;agentic&lt;/strong&gt;, we mean a workflow where the AI operates with autonomy across multiple steps. We avoid using &quot;agent&quot; on its own precisely because of the ambiguity above.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[What makes an MCP server successful?]]></title><description><![CDATA[The rise of MCP: A new standard for AI integration When Anthropic introduced the Model Context Protocol (MCP) in late November 2024, they…]]></description><link>https://handsonarchitects.com/blog/2026/what-makes-mcp-server-successful/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2026/what-makes-mcp-server-successful/</guid><pubDate>Thu, 12 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAADJElEQVR42gEZA+b8AP737P/88v/44OHXyKGPgaCMfdW4lezKmdi3iN7WweHq5NrhycPZz7fJs83ZuOXp0vj27v//+P737P716QD88+Xwt4rEf0VrRCV4RiFnNhVsTCleaFF+d128tJzX39W3ybOtx711omWFomBaWUB4blq0sqz68+j/9usA+MyS2oVDomA7eEcjzp9vvJVunoFZioNnk5SNsqygq6ymqayqrLalhq90iaBrsoBW0IxOY0gqfYmM///0AO7BiKqhh5aMbJJiOJ53VaR7VZZ8XWRmUGlyWXF9anZ+ZV1oVF5fWaq7jqOvgLyYd76rkXthRkReZuPv7wDxyZjJizzcunWNbkZbNSJlQStjUDpJYWVldm1hcmd6g3Bfc2s8UV7h4tC7tIORXjCsdUTViESRpKHG4uoA/+TE97hnxoA6oVoogUIddT0bon1cenxylph7mZt9m5x7j5V8bnRw0sa24ujZe3BdkWE6TnmGeqzA6enhAP7//MSUeK5jK69tOM5ZDshVC7xSErmgh8W8q8W9rMG5qcfAr7ijhM2JTKavrDuKrDuCoTeWwj6StcfT0gDk18jMhEXNhUXjaRP/awbzagqgakSkoo3Kw6zWyrHPxazb0bjMvqadfFtPd4FAlrtemq1jjpc1jLKRtL8A7uLT2KyBwloY+2UB/2sE7m8Xv7itsayS1cyy08ix0Mav0smz1cWrq7CsL36mP26Bs3xIuYFOKnaZc6GyAP/99NnX1KlKE/JlBu1gAMxwL9bOxaKigszJr83Gs8rDsPXqztLFrNzSwu3l3GaPnkl8kEt5jChgesjLxgD//fHFx8IQR2UxTFgvQEVNbXzOxr1HW2JWcX5MZ3dOZnJSY2Y6VWTVxrf///9famssREsjQEonODzX1MwA///2j5qgAC5fJ1NxLVZwAT9upqeiWV1gXGVpc3d2eHt5XGZqRVBW3tHE8uviGiwyFiUrMD9BAAoToqOeAP/88L/GxV2Dmam3urjAv2GGmtXW0v/26fjs4Pfs4fbr4Pvv4/zw4//47u3n3nN6epibmLW2snB5ecnGvgIRyoauJ1iUAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Illustration of MCP server architecture connecting AI applications to external tools and data sources&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/06644113d4543d21f3d4c93d628ea15a/3c492/context7-hero-image.png&quot;
        srcset=&quot;/static/06644113d4543d21f3d4c93d628ea15a/ff46a/context7-hero-image.png 325w,
/static/06644113d4543d21f3d4c93d628ea15a/a6d36/context7-hero-image.png 650w,
/static/06644113d4543d21f3d4c93d628ea15a/3c492/context7-hero-image.png 1300w,
/static/06644113d4543d21f3d4c93d628ea15a/71c1d/context7-hero-image.png 1536w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;callout-note&quot;&gt;
💡 For a quick summary, jump to the &lt;a href=&quot;#tldr&quot;&gt;TL;DR section&lt;/a&gt;.
&lt;/div&gt;
&lt;h2 id=&quot;the-rise-of-mcp-a-new-standard-for-ai-integration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-rise-of-mcp-a-new-standard-for-ai-integration&quot; aria-label=&quot;the rise of mcp a new standard for ai integration permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The rise of MCP: A new standard for AI integration&lt;/h2&gt;
&lt;p&gt;When Anthropic &lt;a href=&quot;https://www.anthropic.com/news/model-context-protocol&quot;&gt;introduced the Model Context Protocol&lt;/a&gt; (MCP) in late November 2024, they pitched it with a simple but powerful analogy: MCP is the &quot;USB-C port of AI applications.&quot; Just as USB-C created a universal connection standard for physical devices, MCP creates a universal extension point for Large Language Models (LLMs). It lets LLMs connect to external tools and data sources - databases, search engines, ticketing systems, and countless others - in a standardized way.&lt;/p&gt;
&lt;p&gt;Before MCP, every AI application needed custom connectors for each external system - 5 AI tools and 100 services meant 500 potential integrations. MCP turned this M×N problem into an M+N one: each AI tool implements the MCP client protocol once, each service implements the MCP server protocol once, and they all interoperate seamlessly.&lt;/p&gt;
&lt;p&gt;What happened next surprised even MCP&apos;s creators. In November 2024, Anthropic open-sourced the protocol with comprehensive specifications, SDKs for Python, TypeScript, Java, Kotlin, and C#, and reference implementations.&lt;/p&gt;
&lt;p&gt;Then adoption accelerated rapidly. Cursor, Windsurf, and Claude Code added support in early 2025. By April, VS Code, GitHub, and OpenAI had all adopted MCP. Cloud infrastructure providers like AWS, Google Cloud, and Microsoft Azure integrated it into their AI offerings. Even specialized tools like Figma and Stripe built their own MCP servers.&lt;/p&gt;
&lt;p&gt;But with thousands of MCP servers now in existence, a critical question emerges: what sets the truly successful ones apart from the thousands that remain unused? To answer that, we need to look at where successful MCP servers live, what problems they solve, and how they are built.&lt;/p&gt;
&lt;h2 id=&quot;successful-mcp-servers-where-to-find-them&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#successful-mcp-servers-where-to-find-them&quot; aria-label=&quot;successful mcp servers where to find them permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Successful MCP servers: Where to find them&lt;/h2&gt;
&lt;p&gt;The MCP ecosystem has grown so rapidly that several dedicated directories have emerged to help developers discover MCP servers. &lt;a href=&quot;https://mcphub.io/&quot;&gt;MCP Hub&lt;/a&gt;, &lt;a href=&quot;https://mcpmarket.com&quot;&gt;mcpmarket.com&lt;/a&gt;, &lt;a href=&quot;https://glama.ai/mcp&quot;&gt;glama.ai/mcp&lt;/a&gt;, and &lt;a href=&quot;https://aiagentslist.com/mcp-servers&quot;&gt;aiagentslist.com/mcp-servers&lt;/a&gt; all curate collections of available servers with descriptions, installation instructions, and usage statistics.&lt;/p&gt;
&lt;p&gt;But raw counts of MCP servers tell only part of the story. The more interesting question is which servers have actually earned developer trust. When we &lt;a href=&quot;https://github.com/search?q=MCP+server&amp;#x26;type=repositories&amp;#x26;s=stars&amp;#x26;o=desc&quot;&gt;rank MCP servers by GitHub stars&lt;/a&gt;, a clear picture of the ecosystem&apos;s leaders emerges:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rank&lt;/th&gt;
&lt;th&gt;MCP Server&lt;/th&gt;
&lt;th&gt;GitHub Stars&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/upstash/context7&quot;&gt;upstash/context7&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;44,000+&lt;/td&gt;
&lt;td&gt;Up-to-date documentation for AI coding assistants (as of January 2026)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/mindsdb/mindsdb&quot;&gt;mindsdb/mindsdb&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;30,000+&lt;/td&gt;
&lt;td&gt;AI integration with databases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/1Panel-dev/1Panel&quot;&gt;1Panel-dev/1Panel&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;25,000+&lt;/td&gt;
&lt;td&gt;Server management platform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/github/github-mcp-server&quot;&gt;github/github-mcp-server&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;20,000+&lt;/td&gt;
&lt;td&gt;GitHub repository interaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/microsoft/playwright-mcp&quot;&gt;microsoft/playwright-mcp&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;15,000+&lt;/td&gt;
&lt;td&gt;Browser automation for AI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Context7, developed by Upstash, stands out with a substantial lead of over 44,000 GitHub stars and over 240,000 weekly npm downloads. That kind of adoption doesn&apos;t happen by accident. Context7 has found genuine product-market fit - solving a problem that developers encounter daily and solving it well enough that adoption grows organically.&lt;/p&gt;
&lt;p&gt;This makes Context7 an ideal case study for understanding what makes an MCP server successful. The following sections examine the problem it solves, the architecture behind it, how it implements the MCP protocol, and the key factors that drove its adoption.&lt;/p&gt;
&lt;h2 id=&quot;context7-what-problem-does-it-solve&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#context7-what-problem-does-it-solve&quot; aria-label=&quot;context7 what problem does it solve permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Context7: What problem does it solve?&lt;/h2&gt;
&lt;p&gt;Every developer who has used an AI coding assistant has experienced the same frustration. You ask the AI to generate code for a library you are working with, and what comes back looks convincing - but doesn&apos;t compile. The method names are wrong. The API has changed since the model was trained. The code mixes patterns from two different major versions of the same framework. You end up spending more time debugging the AI&apos;s output than you would have spent writing the code yourself.&lt;/p&gt;
&lt;p&gt;This frustration arises from four fundamental problems that affect AI coding assistants today.&lt;/p&gt;
&lt;h3 id=&quot;outdated-training-data&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#outdated-training-data&quot; aria-label=&quot;outdated training data permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Outdated training data&lt;/h3&gt;
&lt;p&gt;Large Language Models are trained on documentation snapshots that are typically 6 to 12 months behind the current releases. If you are working with Spring Boot 3.5, but the AI was trained on Spring Boot 2.7 documentation, it confidently generates code using patterns, annotations, and configuration styles that are outdated or deprecated.&lt;/p&gt;
&lt;h3 id=&quot;hallucinated-apis&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hallucinated-apis&quot; aria-label=&quot;hallucinated apis permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Hallucinated APIs&lt;/h3&gt;
&lt;p&gt;The most frustrating problem is when the AI confidently suggests methods, classes, or annotations that don&apos;t exist. You paste the generated code into your IDE, it doesn&apos;t compile, and you waste time debugging phantom APIs that the model fabricated from patterns it learned during training. For example, a model might suggest a JUnit 5 annotation like &lt;code class=&quot;language-text&quot;&gt;@TestOrder&lt;/code&gt; that looks perfectly reasonable but was never part of the JUnit API.&lt;/p&gt;
&lt;h3 id=&quot;generic-answers-without-specifics&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#generic-answers-without-specifics&quot; aria-label=&quot;generic answers without specifics permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Generic answers without specifics&lt;/h3&gt;
&lt;p&gt;When you ask an AI how to implement OAuth2 in your application, you often get a high-level explanation that reads more like a textbook than working code. &quot;Use Spring Security&apos;s OAuth2 support&quot; is technically correct but practically useless when you need the specific configuration classes, filter chain setup, and property values for your framework version.&lt;/p&gt;
&lt;h3 id=&quot;version-conflicts&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#version-conflicts&quot; aria-label=&quot;version conflicts permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Version conflicts&lt;/h3&gt;
&lt;p&gt;The AI frequently mixes APIs from different library versions in the same code snippet, combining deprecated methods alongside current ones. This creates code that is broken from the start - perhaps using &lt;code class=&quot;language-text&quot;&gt;javax.persistence&lt;/code&gt; annotations in a Spring Boot 3.x project that requires &lt;code class=&quot;language-text&quot;&gt;jakarta.persistence&lt;/code&gt;, or mixing Spring Security&apos;s old &lt;code class=&quot;language-text&quot;&gt;WebSecurityConfigurerAdapter&lt;/code&gt; pattern with the newer &lt;code class=&quot;language-text&quot;&gt;SecurityFilterChain&lt;/code&gt; approach.&lt;/p&gt;
&lt;h3 id=&quot;how-context7-solves-this&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-context7-solves-this&quot; aria-label=&quot;how context7 solves this permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How Context7 solves this&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/upstash/context7&quot;&gt;Context7&lt;/a&gt; addresses all four problems with a straightforward approach: it gives AI coding assistants real-time access to the latest official documentation and code examples. Instead of relying on what the model memorized during training, Context7 pulls current, version-specific documentation from framework source repositories and injects it directly into the LLM&apos;s context window at the moment of prompting. The AI then generates code based on this fresh, authoritative information rather than its outdated training data.&lt;/p&gt;
&lt;p&gt;The significance of this approach hasn&apos;t gone unnoticed by the industry. &lt;a href=&quot;https://www.thoughtworks.com/radar/tools/context7&quot;&gt;ThoughtWorks Technology Radar&lt;/a&gt; recently added Context7 to its &quot;Tools&quot; section, noting:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Context7&lt;/strong&gt; is an MCP server that addresses inaccuracies in AI-generated code. While LLMs rely on outdated training data, Context7 ensures they generate accurate, up-to-date and version-specific code for the libraries and frameworks used in a project. It does this by pulling the latest documentation and functional code examples directly from framework source repositories and injecting them into the LLM&apos;s context window at the moment of prompting.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Context7 benefits more than individual developers writing code. Automated code review tools produce better suggestions when they access current documentation. AI code assistants like Cursor, Windsurf, and Claude Code generate more accurate output. Even teams performing large-scale code migrations between framework versions benefit, because Context7 ensures the AI understands the specific APIs available in both the source and target versions.&lt;/p&gt;
&lt;h2 id=&quot;architecture-behind-context7&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#architecture-behind-context7&quot; aria-label=&quot;architecture behind context7 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Architecture behind Context7&lt;/h2&gt;
&lt;p&gt;Understanding why Context7 performs well requires looking beneath the surface at the Upstash platform that powers it. Context7 is a Software as a Service (SaaS) product, and like any successful SaaS, its architecture must balance competing concerns: scalability, resilience, performance, agility, and cost-effectiveness. Upstash&apos;s architects made design choices that reveal how they prioritized and resolved these concerns.&lt;/p&gt;
&lt;h3 id=&quot;high-level-architecture&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#high-level-architecture&quot; aria-label=&quot;high level architecture permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;High-level architecture&lt;/h3&gt;
&lt;p&gt;Context7&apos;s architecture is built around a pipeline that transforms raw documentation from thousands of GitHub repositories and documentation sites into AI-optimized code snippets that can be retrieved in milliseconds.&lt;/p&gt;
&lt;p&gt;&lt;svg id=&quot;mermaid-0&quot; width=&quot;100%&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; class=&quot;flowchart&quot; style=&quot;max-width: 1650.984375px;&quot; viewBox=&quot;0 0 1650.984375 550.8848876953125&quot; role=&quot;graphics-document document&quot; aria-roledescription=&quot;flowchart-v2&quot;&gt;&lt;style&gt;#mermaid-0{font-family:arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-0 .error-icon{fill:#552222;}#mermaid-0 .error-text{fill:#552222;stroke:#552222;}#mermaid-0 .edge-thickness-normal{stroke-width:1px;}#mermaid-0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-0 .marker{fill:#333333;stroke:#333333;}#mermaid-0 .marker.cross{stroke:#333333;}#mermaid-0 svg{font-family:arial,sans-serif;font-size:16px;}#mermaid-0 p{margin:0;}#mermaid-0 .label{font-family:arial,sans-serif;color:#333;}#mermaid-0 .cluster-label text{fill:#333;}#mermaid-0 .cluster-label span{color:#333;}#mermaid-0 .cluster-label span p{background-color:transparent;}#mermaid-0 .label text,#mermaid-0 span{fill:#333;color:#333;}#mermaid-0 .node rect,#mermaid-0 .node circle,#mermaid-0 .node ellipse,#mermaid-0 .node polygon,#mermaid-0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-0 .rough-node .label text,#mermaid-0 .node .label text,#mermaid-0 .image-shape .label,#mermaid-0 .icon-shape .label{text-anchor:middle;}#mermaid-0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-0 .rough-node .label,#mermaid-0 .node .label,#mermaid-0 .image-shape .label,#mermaid-0 .icon-shape .label{text-align:center;}#mermaid-0 .node.clickable{cursor:pointer;}#mermaid-0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-0 .arrowheadPath{fill:#333333;}#mermaid-0 .edgePath .path{stroke:#333333;stroke-width:1px;}#mermaid-0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-0 .cluster text{fill:#333;}#mermaid-0 .cluster span{color:#333;}#mermaid-0 .node .collapsed-indicator{fill:#aaaa33;stroke:none;opacity:0.6;}#mermaid-0 .node .collapsed-separator{stroke:#aaaa33;stroke-width:0.75px;}#mermaid-0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-0 rect.text{fill:none;stroke-width:0;}#mermaid-0 .icon-shape,#mermaid-0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-0 .icon-shape p,#mermaid-0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-0 .icon-shape .label rect,#mermaid-0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-0 .node .neo-node{stroke:#9370DB;}#mermaid-0 [data-look=&quot;neo&quot;].node rect,#mermaid-0 [data-look=&quot;neo&quot;].cluster rect,#mermaid-0 [data-look=&quot;neo&quot;].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].swimlane.cluster rect{filter:none;}#mermaid-0 [data-look=&quot;neo&quot;].node path{stroke:#9370DB;stroke-width:1px;}#mermaid-0 [data-look=&quot;neo&quot;].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].node .neo-line path{stroke:#9370DB;filter:none;}#mermaid-0 [data-look=&quot;neo&quot;].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].node circle .state-start{fill:#000000;}#mermaid-0 [data-look=&quot;neo&quot;].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 :root{--mermaid-font-family:arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-pointEnd&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-pointStart&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-pointEnd-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 11.5 14&quot; refX=&quot;11.5&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;10.5&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 11.5 7 L 0 14 z&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-pointStart-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 11.5 14&quot; refX=&quot;1&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11.5&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;polygon points=&quot;0,7 11.5,14 11.5,0&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/polygon&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-circleEnd&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;11&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-circleStart&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;-1&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-circleEnd-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refY=&quot;5&quot; refX=&quot;12.25&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;14&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-circleStart-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;-2&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;14&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-crossEnd&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 11 11&quot; refX=&quot;12&quot; refY=&quot;5.2&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 l 9,9 M 10,1 l -9,9&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-crossStart&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 11 11&quot; refX=&quot;-1&quot; refY=&quot;5.2&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 l 9,9 M 10,1 l -9,9&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-crossEnd-margin&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 15 15&quot; refX=&quot;17.7&quot; refY=&quot;7.5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 L 14,14 M 1,14 L 14,1&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2.5;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-crossStart-margin&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 15 15&quot; refX=&quot;-3.5&quot; refY=&quot;7.5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 L 14,14 M 1,14 L 14,1&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2.5; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;g class=&quot;root&quot;&gt;&lt;g class=&quot;clusters&quot;&gt;&lt;g class=&quot;cluster&quot; id=&quot;mermaid-0-DATA&quot; data-look=&quot;classic&quot;&gt;&lt;rect style=&quot;&quot; x=&quot;896.90625&quot; y=&quot;8&quot; width=&quot;746.078125&quot; height=&quot;183.88491439819336&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;cluster-label&quot; transform=&quot;translate(1230.8125, 8)&quot;&gt;&lt;foreignObject width=&quot;78.265625&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Data Layer&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;cluster&quot; id=&quot;mermaid-0-BACKEND&quot; data-look=&quot;classic&quot;&gt;&lt;rect style=&quot;&quot; x=&quot;736.109375&quot; y=&quot;390.88491439819336&quot; width=&quot;640.8125&quot; height=&quot;152&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;cluster-label&quot; transform=&quot;translate(991.140625, 390.88491439819336)&quot;&gt;&lt;foreignObject width=&quot;130.75&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Context7 Backend&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;cluster&quot; id=&quot;mermaid-0-SERVER&quot; data-look=&quot;classic&quot;&gt;&lt;rect style=&quot;&quot; x=&quot;405.40625&quot; y=&quot;384.88491439819336&quot; width=&quot;228.25&quot; height=&quot;148&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;cluster-label&quot; transform=&quot;translate(476.109375, 384.88491439819336)&quot;&gt;&lt;foreignObject width=&quot;86.84375&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;MCP Server&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;cluster&quot; id=&quot;mermaid-0-CLIENT&quot; data-look=&quot;classic&quot;&gt;&lt;rect style=&quot;&quot; x=&quot;8&quot; y=&quot;384.88491439819336&quot; width=&quot;265.625&quot; height=&quot;148&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;cluster-label&quot; transform=&quot;translate(102.5703125, 384.88491439819336)&quot;&gt;&lt;foreignObject width=&quot;76.484375&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;LLM Client&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgePaths&quot;&gt;&lt;path d=&quot;M248.625,458.885L252.792,458.885C256.958,458.885,265.292,458.885,280.44,458.885C295.589,458.885,317.552,458.885,339.516,458.885C361.479,458.885,383.443,458.885,397.924,458.885C412.406,458.885,419.406,458.885,422.906,458.885L426.406,458.885&quot; id=&quot;mermaid-0-L_IDE_TOOLS_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_IDE_TOOLS_0&quot; data-points=&quot;W3sieCI6MjQ4LjYyNSwieSI6NDU4Ljg4NDkxNDM5ODE5MzM2fSx7IngiOjI3My42MjUsInkiOjQ1OC44ODQ5MTQzOTgxOTMzNn0seyJ4IjozMzkuNTE1NjI1LCJ5Ijo0NTguODg0OTE0Mzk4MTkzMzZ9LHsieCI6NDA1LjQwNjI1LCJ5Ijo0NTguODg0OTE0Mzk4MTkzMzZ9LHsieCI6NDMwLjQwNjI1LCJ5Ijo0NTguODg0OTE0Mzk4MTkzMzZ9XQ==&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M608.656,458.885L612.823,458.885C616.99,458.885,625.323,458.885,638.027,458.885C650.732,458.885,667.807,458.885,684.883,458.885C701.958,458.885,719.034,458.885,731.072,458.885C743.109,458.885,750.109,458.885,753.609,458.885L757.109,458.885&quot; id=&quot;mermaid-0-L_TOOLS_API_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_TOOLS_API_0&quot; data-points=&quot;W3sieCI6NjA4LjY1NjI1LCJ5Ijo0NTguODg0OTE0Mzk4MTkzMzZ9LHsieCI6NjMzLjY1NjI1LCJ5Ijo0NTguODg0OTE0Mzk4MTkzMzZ9LHsieCI6Njg0Ljg4MjgxMjUsInkiOjQ1OC44ODQ5MTQzOTgxOTMzNn0seyJ4Ijo3MzYuMTA5Mzc1LCJ5Ijo0NTguODg0OTE0Mzk4MTkzMzZ9LHsieCI6NzYxLjEwOTM3NSwieSI6NDU4Ljg4NDkxNDM5ODE5MzM2fV0=&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M846.906,441.826L851.073,440.169C855.24,438.512,863.573,435.199,871.906,433.542C880.24,431.885,888.573,431.885,926.523,383.709C964.473,335.533,1032.04,239.181,1065.823,191.005L1099.606,142.829&quot; id=&quot;mermaid-0-L_API_REDIS_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_API_REDIS_0&quot; data-points=&quot;W3sieCI6ODQ2LjkwNjI1LCJ5Ijo0NDEuODI2MjMzMDAzNjQ3M30seyJ4Ijo4NzEuOTA2MjUsInkiOjQzMS44ODQ5MTQzOTgxOTMzNn0seyJ4Ijo4OTYuOTA2MjUsInkiOjQzMS44ODQ5MTQzOTgxOTMzNn0seyJ4IjoxMTAxLjkwMjg5MzY3NDM4OCwieSI6MTM5LjU1NDQ2MTIyOTMzNDR9XQ==&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M846.906,465.203L851.073,465.817C855.24,466.43,863.573,467.658,871.906,468.271C880.24,468.885,888.573,468.885,920.775,468.885C952.977,468.885,1009.047,468.885,1037.082,468.885L1065.117,468.885&quot; id=&quot;mermaid-0-L_API_QA_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_API_QA_0&quot; data-points=&quot;W3sieCI6ODQ2LjkwNjI1LCJ5Ijo0NjUuMjAyOTQ0NTQ0MzIxNTV9LHsieCI6ODcxLjkwNjI1LCJ5Ijo0NjguODg0OTE0Mzk4MTkzMzZ9LHsieCI6ODk2LjkwNjI1LCJ5Ijo0NjguODg0OTE0Mzk4MTkzMzZ9LHsieCI6MTA2OS4xMTcxODc1LCJ5Ijo0NjguODg0OTE0Mzk4MTkzMzZ9XQ==&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1194.648,92.507L1225.027,92.507C1255.406,92.507,1316.164,92.507,1359.086,92.507C1402.008,92.507,1427.094,92.507,1451.519,94.084C1475.944,95.661,1499.708,98.816,1511.59,100.393L1523.472,101.971&quot; id=&quot;mermaid-0-L_REDIS_VECTOR_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_REDIS_VECTOR_0&quot; data-points=&quot;W3sieCI6MTE5NC42NDg0Mzc1LCJ5Ijo5Mi41MDY4MjA2Nzg3MTA5NH0seyJ4IjoxMzc2LjkyMTg3NSwieSI6OTIuNTA2ODIwNjc4NzEwOTR9LHsieCI6MTQ1Mi4xNzk2ODc1LCJ5Ijo5Mi41MDY4MjA2Nzg3MTA5NH0seyJ4IjoxNTI3LjQzNzUsInkiOjEwMi40OTY5Njg0NjE5NjIxN31d&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1235.752,226.885L1259.281,211.531C1282.809,196.177,1329.865,165.468,1365.937,150.114C1402.008,134.76,1427.094,134.76,1451.528,132.17C1475.963,129.58,1499.746,124.4,1511.638,121.809L1523.529,119.219&quot; id=&quot;mermaid-0-L_DOCS_VECTOR_0&quot; class=&quot;edge-thickness-normal edge-pattern-dotted edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_DOCS_VECTOR_0&quot; data-points=&quot;W3sieCI6MTIzNS43NTIyNzYwNjk5NDUsInkiOjIyNi44ODQ5MTQzOTgxOTMzNn0seyJ4IjoxMzc2LjkyMTg3NSwieSI6MTM0Ljc2MDIzMTAxODA2NjR9LHsieCI6MTQ1Mi4xNzk2ODc1LCJ5IjoxMzQuNzYwMjMxMDE4MDY2NH0seyJ4IjoxNTI3LjQzNzUsInkiOjExOC4zNjgwMTU0NDkwMzUzNH1d&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabels&quot;&gt;&lt;g class=&quot;edgeLabel&quot; transform=&quot;translate(339.515625, 458.88491439819336)&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_IDE_TOOLS_0&quot; transform=&quot;translate(-40.890625, -12)&quot;&gt;&lt;foreignObject width=&quot;81.78125&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;p&gt;JSON-RPC&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabel&quot; transform=&quot;translate(684.8828125, 458.88491439819336)&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_TOOLS_API_0&quot; transform=&quot;translate(-26.2265625, -12)&quot;&gt;&lt;foreignObject width=&quot;52.453125&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;p&gt;HTTPS&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabel&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_API_REDIS_0&quot; transform=&quot;translate(0, 0)&quot;&gt;&lt;foreignObject width=&quot;0&quot; height=&quot;0&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabel&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_API_QA_0&quot; transform=&quot;translate(0, 0)&quot;&gt;&lt;foreignObject width=&quot;0&quot; height=&quot;0&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabel&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_REDIS_VECTOR_0&quot; transform=&quot;translate(0, 0)&quot;&gt;&lt;foreignObject width=&quot;0&quot; height=&quot;0&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabel&quot; transform=&quot;translate(1452.1796875, 134.7602310180664)&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_DOCS_VECTOR_0&quot; transform=&quot;translate(-50.2578125, -12)&quot;&gt;&lt;foreignObject width=&quot;100.515625&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;p&gt;Index Pipeline&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;nodes&quot;&gt;&lt;g class=&quot;root&quot; transform=&quot;translate(913.90625, 218.88491439819336)&quot;&gt;&lt;g class=&quot;clusters&quot;&gt;&lt;g class=&quot;cluster&quot; id=&quot;mermaid-0-DOCS&quot; data-look=&quot;classic&quot;&gt;&lt;rect style=&quot;&quot; x=&quot;8&quot; y=&quot;8&quot; width=&quot;430.015625&quot; height=&quot;129&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;cluster-label&quot; transform=&quot;translate(137.625, 8)&quot;&gt;&lt;foreignObject width=&quot;170.765625&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Documentation Sources&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgePaths&quot;&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabels&quot;&gt;&lt;/g&gt;&lt;g class=&quot;nodes&quot;&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-REPOS-4&quot; data-look=&quot;classic&quot; transform=&quot;translate(123.25, 72.5)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;&quot; x=&quot;-80.25&quot; y=&quot;-27&quot; width=&quot;160.5&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-50.25, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;100.5&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;GitHub Repos&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-OFFICIAL-5&quot; data-look=&quot;classic&quot; transform=&quot;translate(328.2578125, 72.5)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;&quot; x=&quot;-74.7578125&quot; y=&quot;-27&quot; width=&quot;149.515625&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-44.7578125, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;89.515625&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Official Docs&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-IDE-0&quot; data-look=&quot;classic&quot; transform=&quot;translate(140.8125, 458.88491439819336)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;stroke:#3b82f6 !important;stroke-width:2px !important&quot; x=&quot;-107.8125&quot; y=&quot;-39&quot; width=&quot;215.625&quot; height=&quot;78&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-77.8125, -24)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;155.625&quot; height=&quot;48&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Cursor · Claude Code&lt;br&gt;&lt;/br&gt;VS Code · Windsurf&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-TOOLS-1&quot; data-look=&quot;classic&quot; transform=&quot;translate(519.53125, 458.88491439819336)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;stroke:#e8833a !important;stroke-width:2px !important&quot; x=&quot;-89.125&quot; y=&quot;-39&quot; width=&quot;178.25&quot; height=&quot;78&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-59.125, -24)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;118.25&quot; height=&quot;48&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;resolve-library-id&lt;br&gt;&lt;/br&gt;query-docs&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-API-2&quot; data-look=&quot;classic&quot; transform=&quot;translate(804.0078125, 458.88491439819336)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;stroke:#10b981 !important;stroke-width:2px !important&quot; x=&quot;-42.8984375&quot; y=&quot;-27&quot; width=&quot;85.796875&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-12.8984375, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;25.796875&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;API&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-QA-3&quot; data-look=&quot;classic&quot; transform=&quot;translate(1136.9140625, 468.88491439819336)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;stroke:#10b981 !important;stroke-width:2px !important&quot; x=&quot;-67.796875&quot; y=&quot;-39&quot; width=&quot;135.59375&quot; height=&quot;78&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-37.796875, -24)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;75.59375&quot; height=&quot;48&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Quality&lt;br&gt;&lt;/br&gt;Assurance&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-VECTOR-6&quot; data-look=&quot;classic&quot; transform=&quot;translate(1572.7109375, 108.50682067871094)&quot;&gt;&lt;path d=&quot;M0,10.501993475897065 a45.2734375,10.501993475897065 0,0,0 90.546875,0 a45.2734375,10.501993475897065 0,0,0 -90.546875,0 l0,73.50199347589707 a45.2734375,10.501993475897065 0,0,0 90.546875,0 l0,-73.50199347589707&quot; class=&quot;basic label-container outer-path&quot; style=&quot;stroke:#9b72cf !important;stroke-width:2px !important&quot; transform=&quot;translate(-45.2734375, -47.2529902138456)&quot;&gt;&lt;/path&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-37.7734375, -14)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;75.546875&quot; height=&quot;48&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Vector DB&lt;br&gt;&lt;/br&gt;(DiskANN)&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-REDIS-7&quot; data-look=&quot;classic&quot; transform=&quot;translate(1136.9140625, 92.50682067871094)&quot;&gt;&lt;path d=&quot;M0,12.004548408057179 a57.734375,12.004548408057179 0,0,0 115.46875,0 a57.734375,12.004548408057179 0,0,0 -115.46875,0 l0,75.00454840805718 a57.734375,12.004548408057179 0,0,0 115.46875,0 l0,-75.00454840805718&quot; class=&quot;basic label-container outer-path&quot; style=&quot;stroke:#9b72cf !important;stroke-width:2px !important&quot; transform=&quot;translate(-57.734375, -49.50682261208577)&quot;&gt;&lt;/path&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-50.234375, -14)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;100.46875&quot; height=&quot;48&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Redis Cache&lt;br&gt;&lt;/br&gt;(Multi-Region)&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;defs&gt;&lt;filter id=&quot;mermaid-0-drop-shadow&quot; height=&quot;130%&quot; width=&quot;130%&quot;&gt;&lt;feDropShadow dx=&quot;4&quot; dy=&quot;4&quot; stdDeviation=&quot;0&quot; flood-opacity=&quot;0.06&quot; flood-color=&quot;#000000&quot;&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;defs&gt;&lt;filter id=&quot;mermaid-0-drop-shadow-small&quot; height=&quot;150%&quot; width=&quot;150%&quot;&gt;&lt;feDropShadow dx=&quot;2&quot; dy=&quot;2&quot; stdDeviation=&quot;0&quot; flood-opacity=&quot;0.06&quot; flood-color=&quot;#000000&quot;&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;/svg&gt;&lt;/p&gt;
&lt;p&gt;The system comprises several interconnected components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LLM Client&lt;/strong&gt; - The AI application (Cursor, Windsurf, Claude Desktop, Claude Code) that needs documentation context.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context7 MCP Server&lt;/strong&gt; - The protocol adapter that translates MCP tool calls into Context7 API requests.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API Backend&lt;/strong&gt; - The service layer that orchestrates library resolution and documentation retrieval.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Upstash Vector Database&lt;/strong&gt; - Stores processed documentation as vector embeddings using the DiskANN (Disk-based Approximate Nearest Neighbor) algorithm. DiskANN keeps indexes on disk rather than RAM, enabling cost-effective semantic search across 33,000+ libraries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redis Caching Layer (Upstash Global Database)&lt;/strong&gt; - A multi-region distributed cache for frequently accessed documentation, providing sub-millisecond reads from the nearest region.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quality Assurance System&lt;/strong&gt; - Validation and scoring infrastructure that ensures documentation quality through source reputation, benchmark scoring, and injection prevention.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;scalability-and-elasticity&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#scalability-and-elasticity&quot; aria-label=&quot;scalability and elasticity permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Scalability and elasticity&lt;/h3&gt;
&lt;p&gt;For a system serving thousands of concurrent developers across the globe, scalability is essential. Context7 needs to handle not just steady growth in users, but also the bursty, unpredictable traffic spikes common in LLM-driven applications.&lt;/p&gt;
&lt;p&gt;Upstash addresses this through a combination of serverless infrastructure and intelligent architectural choices. The vector database uses DiskANN and its variant FreshDiskANN (optimized for incremental index updates), which store vector indexes on disk rather than requiring everything in memory. This approach keeps memory requirements constant regardless of dataset size, allowing the system to scale to millions of documentation snippets without proportional cost increases.&lt;/p&gt;
&lt;h3 id=&quot;resilience-and-fault-tolerance&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#resilience-and-fault-tolerance&quot; aria-label=&quot;resilience and fault tolerance permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Resilience and fault tolerance&lt;/h3&gt;
&lt;p&gt;Context7&apos;s caching layer uses Upstash Global Database - a multi-region Redis deployment where a primary region handles all writes while read replicas distributed globally serve read requests. If a region becomes unavailable, requests automatically route to the next-nearest region.&lt;/p&gt;
&lt;p&gt;The caching layer uses eventual consistency, prioritizing read performance over immediate consistency. With documentation updated every 10 to 15 days, brief cache staleness during propagation is negligible.&lt;/p&gt;
&lt;h3 id=&quot;performance-through-server-side-intelligence&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#performance-through-server-side-intelligence&quot; aria-label=&quot;performance through server side intelligence permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Performance through server-side intelligence&lt;/h3&gt;
&lt;p&gt;One of Context7&apos;s most impactful architectural decisions was moving documentation filtering and ranking from the LLM client to the server side. In the original architecture, the API returned 50-100 vector search results and left it to the LLM to filter and rank them - a process that averaged 9,700 tokens, 3.95 tool calls, and 24 seconds of latency.&lt;/p&gt;
&lt;p&gt;The current architecture performs vector search plus server-side reranking (re-ordering results by relevance) using a &lt;a href=&quot;https://upstash.com/blog/new-context7&quot;&gt;proprietary 5-metric scoring system&lt;/a&gt;. The result: an average of just 3,300 tokens, 2.96 tool calls, and 15 seconds of latency - a 65% reduction in token consumption and 38% reduction in latency. The server does more work per request, but that work uses specialized, cost-efficient models rather than expensive general-purpose LLMs.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.46153846153847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACD0lEQVR42n2T7UtTYRiH93f2LSMIoqJACvpcSYaYqdWXGpWOYGKRoMu9ZOSWNqYEkam0CtG0ne3s7OzsvF/dZ+fszUUPPAeem/O7nt/98sQ4vTwPDo6xjRaWbYfbcfhR99FaNrZlYUdx3/eH5LH2VwToBrQsCH5qmbgSc4Kt6Th1nWbLwXa9MO7KlgvbGsv+B/DzHkwmYCErDvtuNUyIL8GjBfj1u5dBsN5vwcQcZIvhOXIbAre+wZ04zKdCQSeVpriYTYpwHsqHg8D0Jtx6AqmPeO2wT6CK+UPAPocC9GbF3VSC0sZP7m2bLO4020IyPeCAw6Yn9dve7QJ91+8aDIBuAJxOsJL7zplUg/GC1gPefoq+VCC5b5P4YnCse8QM3x0AnnbYAabXypzL6Ex9ioDpEFh5ledSrsH55RpfFWc45XrLpWJ4mF7YlA5w9V2ZkbTO/c1BYPV1nhvrOlczKntVJ2pKSYBjcdy5FNPFBtdydQpHUgrbxJ5J/heoCPD6B50raZXdDtAv7bQdui9WGMtrjCyrrB1EwGkBPgiBZwU42Q+UpiiS8qgAL/cD2zUcf46TeMvEhsbF1RrrhwFQXsXjRXj4kozU8EK2wUwxAuZk/u4+Q3lT4KakPJpV2e8CbZl8RcWo1PijSbc0E8sNR8HXmpgnCqq8lqO6iWZGc2haOBWVZrXOScOiarg4HvwFcKNo7Sgq0gUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Grouped bar chart comparing before and after server-side reranking: tokens dropped from 9,700 to 3,300 (66% reduction), tool calls from 3.95 to 2.96 (25% reduction), and latency from 24s to 15s (38% reduction)&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/6c8982fa72fad6764bc71330ef901ec2/5a190/server-side-reranking-optimization.png&quot;
        srcset=&quot;/static/6c8982fa72fad6764bc71330ef901ec2/ff46a/server-side-reranking-optimization.png 325w,
/static/6c8982fa72fad6764bc71330ef901ec2/a6d36/server-side-reranking-optimization.png 650w,
/static/6c8982fa72fad6764bc71330ef901ec2/5a190/server-side-reranking-optimization.png 800w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.46153846153847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAAC3ElEQVR42n2SXUhTYRjHj/mJNU3DvAm1EsTPotryC4S6iEAohN2YczubKGb2ZSAadNzZ55loTpcx3NzcUYuTmUkNrWThF9hNN+siY0QXbXO6GXVhmu7pPWdO0qIX3ovned7/j//zvA+G7TlQRkRBNVHAdPQlE4zjAGUa4xHj4/EZpuUsoXk6xWSieBRl4hG0PQEAIrB/nWCjPhZE6kMgpXiAYREoTrDb7bHs9TT1Hva29KXe6nMmiy2OOCfDxDCMM+YzYTnIaWqN8X8BAVdeDEoUdpCSGhAykTv5Wm0iiOVmkJAMiImTXG67jnI1gJOvQCK/ysXYH25BTF5GD+bRgx5WEC5CvSYJcMUIK9yQyPnhkWybuB4Uk+8Q8A4bCxmIxMIjAInyUhAn55Cwe5fDSk3SFq58CqJ7o3rF5O2soW/aUrO7PmRC0YhMLCBN026HAHEgIsqDuJIDGmuN0RgB+8LADVz5DK60MIRupjOP9i8UWTy9XE3KAeeX6zrasm2rskKL76aAXjnCAqNBRO4A9zrclKpGobL1CamZoXLpwFSJxdMZbhmq22ZdDQ90BbaVFwKrd66Q9p3a1fIWmqHQvJiSN/wjFXNCDAibE0MO7zJy7bQuZ9DvKO733Gc1myxQ3Dbnauih8unVQb516eVZq7eAA/6Scp8ysy5V9hZZvTr+gG88m149j1Yoek2mev5/oAEBA4+QwwmBeelEyKFUxQJn13HVw0Krx3jG6pvPHQyUI2DUWo12nG1Zrp3R5dD+t8UWTxcHlJA3EHDe1aDX5dGBx/wB76TAFgbiZDkCvvkpU3eXWN3dfJvvde6Q/wIHlGkYqGodllPT6hw6MFFkdVMhjaIe7ejU4jWDOp/2D6CWx073e/O395CI+9rclTarMB6vs7zPqBj8kJ6pD8ayte+NqpRPhCGz3TCVXjHy8Vjp0JckTlPVtN/d1J4+2W47KmScaeeGXallDoj6DUv+rCSO5AcqAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Grouped bar chart comparing before and after server-side reranking: tokens dropped from 9,700 to 3,300 (66% reduction), tool calls from 3.95 to 2.96 (25% reduction), and latency from 24s to 15s (38% reduction)&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/34cdb98bb8a236abd7dc15c7c87fb616/3c492/server-side-reranking-optimization-dark.png&quot;
        srcset=&quot;/static/34cdb98bb8a236abd7dc15c7c87fb616/ff46a/server-side-reranking-optimization-dark.png 325w,
/static/34cdb98bb8a236abd7dc15c7c87fb616/a6d36/server-side-reranking-optimization-dark.png 650w,
/static/34cdb98bb8a236abd7dc15c7c87fb616/3c492/server-side-reranking-optimization-dark.png 1300w,
/static/34cdb98bb8a236abd7dc15c7c87fb616/29007/server-side-reranking-optimization-dark.png 1600w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;agility-and-cost-effectiveness&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#agility-and-cost-effectiveness&quot; aria-label=&quot;agility and cost effectiveness permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Agility and cost-effectiveness&lt;/h3&gt;
&lt;p&gt;Context7 is composed of multiple focused components - the MCP server, the API backend, the indexing pipeline, the quality assurance system - each of which can be iterated and deployed independently. Moreover, each can be scaled independently based on demand. Combined with Upstash&apos;s serverless infrastructure, each component scales with demand without overprovisioning, and costs scale with actual usage rather than reserved capacity.&lt;/p&gt;
&lt;h2 id=&quot;how-context7-implements-the-mcp-protocol&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-context7-implements-the-mcp-protocol&quot; aria-label=&quot;how context7 implements the mcp protocol permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How Context7 implements the MCP protocol&lt;/h2&gt;
&lt;p&gt;This is where we move from &quot;what&quot; to &quot;how.&quot; Understanding Context7&apos;s MCP implementation reveals the engineering decisions that make it responsive, reliable, and LLM-friendly.&lt;/p&gt;
&lt;h3 id=&quot;the-mcp-data-layer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-mcp-data-layer&quot; aria-label=&quot;the mcp data layer permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The MCP data layer&lt;/h3&gt;
&lt;p&gt;The Model Context Protocol defines three core primitives that MCP servers can expose to LLM clients:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tools&lt;/strong&gt; - Functions that AI models can invoke, such as querying databases, calling APIs, or performing computations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resources&lt;/strong&gt; - Data that provides context to language models, such as files, database schemas, or application-specific information.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prompts&lt;/strong&gt; - Predefined templates or instructions that guide the language model&apos;s responses.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Context7 exposes a minimal MCP interface:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Primitive&lt;/th&gt;
&lt;th&gt;Implementation&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tools&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;resolve-library-id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resolves a package name to a Context7-compatible library ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tools&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;query-docs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Retrieves up-to-date documentation and code examples for a library&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resources&lt;/td&gt;
&lt;td&gt;&lt;em&gt;empty&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;No resources exposed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompts&lt;/td&gt;
&lt;td&gt;&lt;em&gt;empty&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;No prompts exposed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This minimalism is a design choice, not a limitation. By providing just two focused tools, Context7 keeps the integration surface small and predictable. LLM clients don&apos;t need to navigate a complex menu of capabilities - they resolve a library and query its documentation. That&apos;s it.&lt;/p&gt;
&lt;h3 id=&quot;inspecting-the-mcp-implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#inspecting-the-mcp-implementation&quot; aria-label=&quot;inspecting the mcp implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Inspecting the MCP implementation&lt;/h3&gt;
&lt;p&gt;You can examine Context7&apos;s MCP implementation yourself using the &lt;a href=&quot;https://modelcontextprotocol.io/docs/tools/inspector&quot;&gt;MCP Inspector&lt;/a&gt;, an interactive debugging tool for MCP servers. Run the following command to launch the inspector connected to Context7:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt; @modelcontextprotocol/inspector npx &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt; @upstash/context7-mcp
&lt;span class=&quot;token comment&quot;&gt;# Opens MCP Inspector at http://localhost:5173&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This command opens a browser-based inspector at &lt;code class=&quot;language-text&quot;&gt;http://localhost:5173&lt;/code&gt; that shows the complete MCP handshake - the initialization exchange, capability negotiation, tool definitions - and lets you invoke tools interactively to see their responses. It&apos;s an invaluable tool for understanding how any MCP server works internally.&lt;/p&gt;
&lt;h3 id=&quot;protocol-layers-what-the-llm-sees-vs-what-the-server-hides&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#protocol-layers-what-the-llm-sees-vs-what-the-server-hides&quot; aria-label=&quot;protocol layers what the llm sees vs what the server hides permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Protocol layers: What the LLM sees vs. what the server hides&lt;/h3&gt;
&lt;p&gt;Context7&apos;s communication architecture operates across two distinct layers, and understanding this separation is key to understanding MCP&apos;s design philosophy.&lt;/p&gt;
&lt;p&gt;&lt;svg id=&quot;mermaid-1&quot; width=&quot;100%&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; class=&quot;flowchart&quot; style=&quot;max-width: 256px;&quot; viewBox=&quot;0 0 256 654&quot; role=&quot;graphics-document document&quot; aria-roledescription=&quot;flowchart-v2&quot;&gt;&lt;style&gt;#mermaid-1{font-family:arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-1 .error-icon{fill:#552222;}#mermaid-1 .error-text{fill:#552222;stroke:#552222;}#mermaid-1 .edge-thickness-normal{stroke-width:1px;}#mermaid-1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-1 .marker{fill:#333333;stroke:#333333;}#mermaid-1 .marker.cross{stroke:#333333;}#mermaid-1 svg{font-family:arial,sans-serif;font-size:16px;}#mermaid-1 p{margin:0;}#mermaid-1 .label{font-family:arial,sans-serif;color:#333;}#mermaid-1 .cluster-label text{fill:#333;}#mermaid-1 .cluster-label span{color:#333;}#mermaid-1 .cluster-label span p{background-color:transparent;}#mermaid-1 .label text,#mermaid-1 span{fill:#333;color:#333;}#mermaid-1 .node rect,#mermaid-1 .node circle,#mermaid-1 .node ellipse,#mermaid-1 .node polygon,#mermaid-1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-1 .rough-node .label text,#mermaid-1 .node .label text,#mermaid-1 .image-shape .label,#mermaid-1 .icon-shape .label{text-anchor:middle;}#mermaid-1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-1 .rough-node .label,#mermaid-1 .node .label,#mermaid-1 .image-shape .label,#mermaid-1 .icon-shape .label{text-align:center;}#mermaid-1 .node.clickable{cursor:pointer;}#mermaid-1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-1 .arrowheadPath{fill:#333333;}#mermaid-1 .edgePath .path{stroke:#333333;stroke-width:1px;}#mermaid-1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-1 .cluster text{fill:#333;}#mermaid-1 .cluster span{color:#333;}#mermaid-1 .node .collapsed-indicator{fill:#aaaa33;stroke:none;opacity:0.6;}#mermaid-1 .node .collapsed-separator{stroke:#aaaa33;stroke-width:0.75px;}#mermaid-1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-1 rect.text{fill:none;stroke-width:0;}#mermaid-1 .icon-shape,#mermaid-1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-1 .icon-shape p,#mermaid-1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-1 .icon-shape .label rect,#mermaid-1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-1 .node .neo-node{stroke:#9370DB;}#mermaid-1 [data-look=&quot;neo&quot;].node rect,#mermaid-1 [data-look=&quot;neo&quot;].cluster rect,#mermaid-1 [data-look=&quot;neo&quot;].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-1 [data-look=&quot;neo&quot;].swimlane.cluster rect{filter:none;}#mermaid-1 [data-look=&quot;neo&quot;].node path{stroke:#9370DB;stroke-width:1px;}#mermaid-1 [data-look=&quot;neo&quot;].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-1 [data-look=&quot;neo&quot;].node .neo-line path{stroke:#9370DB;filter:none;}#mermaid-1 [data-look=&quot;neo&quot;].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-1 [data-look=&quot;neo&quot;].node circle .state-start{fill:#000000;}#mermaid-1 [data-look=&quot;neo&quot;].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-1 [data-look=&quot;neo&quot;].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-1 :root{--mermaid-font-family:arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;marker id=&quot;mermaid-1_flowchart-v2-pointEnd&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-1_flowchart-v2-pointStart&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-1_flowchart-v2-pointEnd-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 11.5 14&quot; refX=&quot;11.5&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;10.5&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 11.5 7 L 0 14 z&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-1_flowchart-v2-pointStart-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 11.5 14&quot; refX=&quot;1&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11.5&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;polygon points=&quot;0,7 11.5,14 11.5,0&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/polygon&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-1_flowchart-v2-circleEnd&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;11&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-1_flowchart-v2-circleStart&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;-1&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-1_flowchart-v2-circleEnd-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refY=&quot;5&quot; refX=&quot;12.25&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;14&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-1_flowchart-v2-circleStart-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;-2&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;14&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-1_flowchart-v2-crossEnd&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 11 11&quot; refX=&quot;12&quot; refY=&quot;5.2&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 l 9,9 M 10,1 l -9,9&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-1_flowchart-v2-crossStart&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 11 11&quot; refX=&quot;-1&quot; refY=&quot;5.2&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 l 9,9 M 10,1 l -9,9&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-1_flowchart-v2-crossEnd-margin&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 15 15&quot; refX=&quot;17.7&quot; refY=&quot;7.5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 L 14,14 M 1,14 L 14,1&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2.5;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-1_flowchart-v2-crossStart-margin&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 15 15&quot; refX=&quot;-3.5&quot; refY=&quot;7.5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 L 14,14 M 1,14 L 14,1&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2.5; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;g class=&quot;root&quot;&gt;&lt;g class=&quot;clusters&quot;&gt;&lt;/g&gt;&lt;g class=&quot;edgePaths&quot;&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabels&quot;&gt;&lt;/g&gt;&lt;g class=&quot;nodes&quot;&gt;&lt;g class=&quot;root&quot; transform=&quot;translate(0, 0)&quot;&gt;&lt;g class=&quot;clusters&quot;&gt;&lt;g class=&quot;cluster&quot; id=&quot;mermaid-1-L2&quot; data-look=&quot;classic&quot;&gt;&lt;rect style=&quot;&quot; x=&quot;8&quot; y=&quot;8&quot; width=&quot;240&quot; height=&quot;306&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;cluster-label&quot; transform=&quot;translate(70.6484375, 8)&quot;&gt;&lt;foreignObject width=&quot;114.703125&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Layer 2: HTTPS&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgePaths&quot;&gt;&lt;path d=&quot;M128,99.5L128,109.75C128,120,128,140.5,128,160.333C128,180.167,128,199.333,128,208.917L128,218.5&quot; id=&quot;mermaid-1-L_SRV2_C7_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_SRV2_C7_0&quot; data-points=&quot;W3sieCI6MTI4LCJ5Ijo5OS41fSx7IngiOjEyOCwieSI6MTYxfSx7IngiOjEyOCwieSI6MjIyLjV9XQ==&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-1_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabels&quot;&gt;&lt;g class=&quot;edgeLabel&quot; transform=&quot;translate(128, 161)&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_SRV2_C7_0&quot; transform=&quot;translate(-100, -24)&quot;&gt;&lt;foreignObject width=&quot;200&quot; height=&quot;48&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;p&gt;HTTP requests and responses&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;nodes&quot;&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-1-flowchart-SRV2-2&quot; data-look=&quot;classic&quot; transform=&quot;translate(128, 72.5)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;stroke:#e8833a !important;stroke-width:2px !important&quot; x=&quot;-73.421875&quot; y=&quot;-27&quot; width=&quot;146.84375&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-43.421875, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;86.84375&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;MCP Server&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-1-flowchart-C7-3&quot; data-look=&quot;classic&quot; transform=&quot;translate(128, 249.5)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;stroke:#10b981 !important;stroke-width:2px !important&quot; x=&quot;-76.6953125&quot; y=&quot;-27&quot; width=&quot;153.390625&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-46.6953125, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;93.390625&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Context7 API&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;root&quot; transform=&quot;translate(11.578125, 356)&quot;&gt;&lt;g class=&quot;clusters&quot;&gt;&lt;g class=&quot;cluster&quot; id=&quot;mermaid-1-L1&quot; data-look=&quot;classic&quot;&gt;&lt;rect style=&quot;&quot; x=&quot;3.4921875&quot; y=&quot;8&quot; width=&quot;225.859375&quot; height=&quot;282&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;cluster-label&quot; transform=&quot;translate(7.4921875, 8)&quot;&gt;&lt;foreignObject width=&quot;217.859375&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Layer 1: JSON-RPC over stdio&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgePaths&quot;&gt;&lt;path d=&quot;M116.422,99.5L116.422,107.75C116.422,116,116.422,132.5,116.422,148.333C116.422,164.167,116.422,179.333,116.422,186.917L116.422,194.5&quot; id=&quot;mermaid-1-L_LLMC_SRV1_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_LLMC_SRV1_0&quot; data-points=&quot;W3sieCI6MTE2LjQyMTg3NSwieSI6OTkuNX0seyJ4IjoxMTYuNDIxODc1LCJ5IjoxNDl9LHsieCI6MTE2LjQyMTg3NSwieSI6MTk4LjV9XQ==&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-1_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabels&quot;&gt;&lt;g class=&quot;edgeLabel&quot; transform=&quot;translate(116.421875, 149)&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_LLMC_SRV1_0&quot; transform=&quot;translate(-74.2578125, -12)&quot;&gt;&lt;foreignObject width=&quot;148.515625&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;p&gt;Tool calls and results&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;nodes&quot;&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-1-flowchart-LLMC-0&quot; data-look=&quot;classic&quot; transform=&quot;translate(116.421875, 72.5)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;stroke:#3b82f6 !important;stroke-width:2px !important&quot; x=&quot;-68.2421875&quot; y=&quot;-27&quot; width=&quot;136.484375&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-38.2421875, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;76.484375&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;LLM Client&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-1-flowchart-SRV1-1&quot; data-look=&quot;classic&quot; transform=&quot;translate(116.421875, 225.5)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;stroke:#e8833a !important;stroke-width:2px !important&quot; x=&quot;-73.421875&quot; y=&quot;-27&quot; width=&quot;146.84375&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-43.421875, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;86.84375&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;MCP Server&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;defs&gt;&lt;filter id=&quot;mermaid-1-drop-shadow&quot; height=&quot;130%&quot; width=&quot;130%&quot;&gt;&lt;feDropShadow dx=&quot;4&quot; dy=&quot;4&quot; stdDeviation=&quot;0&quot; flood-opacity=&quot;0.06&quot; flood-color=&quot;#000000&quot;&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;defs&gt;&lt;filter id=&quot;mermaid-1-drop-shadow-small&quot; height=&quot;150%&quot; width=&quot;150%&quot;&gt;&lt;feDropShadow dx=&quot;2&quot; dy=&quot;2&quot; stdDeviation=&quot;0&quot; flood-opacity=&quot;0.06&quot; flood-color=&quot;#000000&quot;&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;/svg&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Layer 1: JSON-RPC (JSON Remote Procedure Call) over stdio (standard input/output)&lt;/strong&gt; - This is what the LLM client (Claude, Cursor, etc.) directly interacts with. The client sends JSON-RPC 2.0 messages containing tool names and parameters, and receives structured text responses. The LLM sees tool names, request parameters, tool results, and error messages - nothing else.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Layer 2: HTTPS to Context7 Backend&lt;/strong&gt; - This is what the MCP server handles internally. It translates JSON-RPC tool calls into HTTP requests to Context7&apos;s API, manages authentication headers, handles response codes, and measures latency. None of this is visible to the LLM.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Visible to LLM?&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tool name (&lt;code class=&quot;language-text&quot;&gt;resolve-library-id&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;query-docs&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;JSON-RPC method name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request parameters (JSON)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;libraryName&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;query&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;libraryId&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool result (formatted text)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Library data or documentation snippets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP URL called by MCP server&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Abstracted by MCP server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP response codes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Abstracted by MCP server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request headers and authentication&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Abstracted by MCP server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network latency&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Abstracted by MCP server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The MCP server handles all the complexity of communicating with the Context7 API, allowing the LLM to focus solely on generating accurate queries and interpreting results.&lt;/p&gt;
&lt;h3 id=&quot;mcp-lifecycle-tool-discovery&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mcp-lifecycle-tool-discovery&quot; aria-label=&quot;mcp lifecycle tool discovery permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MCP lifecycle: Tool discovery&lt;/h3&gt;
&lt;p&gt;After initialization, the client calls &lt;code class=&quot;language-text&quot;&gt;tools/list&lt;/code&gt; to discover available tools. Context7&apos;s response reveals two tools - &lt;code class=&quot;language-text&quot;&gt;resolve-library-id&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;query-docs&lt;/code&gt; - with descriptions that are unusually detailed and prescriptive for an MCP server. This is intentional: the descriptions function as behavioral instructions for the LLM. Several aspects stand out:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Privacy guardrails embedded in the schema.&lt;/strong&gt; Both tools include explicit warnings in their parameter descriptions: &lt;em&gt;&quot;IMPORTANT: Do not include any sensitive or confidential information such as API keys, passwords, credentials, or personal data in your query.&quot;&lt;/em&gt; This is a defensive design choice - because query text is sent to Context7&apos;s API, the tool description itself acts as a privacy firewall, instructing the LLM to strip sensitive content before making the call.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Call frequency limits.&lt;/strong&gt; Both descriptions include: &lt;em&gt;&quot;IMPORTANT: Do not call this tool more than 3 times per question.&quot;&lt;/em&gt; This prevents the LLM from entering retry loops that would waste tokens and API quota while degrading user experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Structured selection criteria.&lt;/strong&gt; The &lt;code class=&quot;language-text&quot;&gt;resolve-library-id&lt;/code&gt; description prescribes a ranked selection process for the LLM: name similarity (exact matches first), description relevance, documentation coverage (higher snippet counts preferred), source reputation, and benchmark score (0-100 quality indicator). It even specifies the response format - the LLM should explain why it chose a particular library and acknowledge when multiple good matches exist.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Query quality guidance.&lt;/strong&gt; The &lt;code class=&quot;language-text&quot;&gt;query-docs&lt;/code&gt; tool provides explicit examples of good queries (&lt;em&gt;&quot;How to set up authentication with JWT in Express.js&quot;&lt;/em&gt;) versus bad ones (&lt;em&gt;&quot;auth&quot;&lt;/em&gt;, &lt;em&gt;&quot;hooks&quot;&lt;/em&gt;), coaching the LLM to generate specific, context-rich searches.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MCP annotations.&lt;/strong&gt; Both tools include &lt;code class=&quot;language-text&quot;&gt;&quot;annotations&quot;: { &quot;readOnlyHint&quot;: true }&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;&quot;execution&quot;: { &quot;taskSupport&quot;: &quot;forbidden&quot; }&lt;/code&gt;, signaling to the client that these tools only read data and should not be used in background task execution.&lt;/p&gt;
&lt;h3 id=&quot;api-endpoints&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#api-endpoints&quot; aria-label=&quot;api endpoints permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;API endpoints&lt;/h3&gt;
&lt;p&gt;Behind the MCP protocol layer, Context7&apos;s server communicates with exactly two HTTP API endpoints. These are the real workhorses of the system.&lt;/p&gt;
&lt;h4 id=&quot;endpoint-1-library-resolution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#endpoint-1-library-resolution&quot; aria-label=&quot;endpoint 1 library resolution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Endpoint 1: Library resolution&lt;/h4&gt;
&lt;p&gt;This endpoint powers the &lt;code class=&quot;language-text&quot;&gt;resolve-library-id&lt;/code&gt; tool. It performs fuzzy matching and LLM-based relevance ranking to find libraries matching a developer&apos;s query.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;URL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;https://context7.com/api/v2/libs/search&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Method&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authentication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Optional (Bearer token with &lt;code class=&quot;language-text&quot;&gt;ctx7sk_&lt;/code&gt; prefix)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Query Parameters:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;query&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;The user&apos;s original question, used for relevance ranking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;libraryName&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;The library name to search for&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Example Request:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;http&quot;&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token request-line&quot;&gt;&lt;span class=&quot;token method property&quot;&gt;GET&lt;/span&gt; &lt;span class=&quot;token request-target url&quot;&gt;/api/v2/libs/search?query=How+to+create+a+REST+API+in+Spring+Boot+3.5&amp;amp;libraryName=Spring+Boot&lt;/span&gt; &lt;span class=&quot;token http-version property&quot;&gt;HTTP/1.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;context7.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Authorization&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Bearer ctx7sk_xxx...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;X-Context7-Source&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;mcp-server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;X-Context7-Server-Version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;1.0.23&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;X-Context7-Client-IDE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;claude-code&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;X-Context7-Transport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;stdio&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The response returns an array of matching libraries, each with rich metadata that the LLM uses to select the best match:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;results&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/spring-projects/spring-boot&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Spring Boot&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Spring Boot helps you to create Spring-powered, production-grade applications...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;totalSnippets&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2446&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;trustScore&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;benchmarkScore&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;82.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;versions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;v3.4.1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;v3.5.3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;v3.5.9&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;v4.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;state&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;finalized&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;trustScore&lt;/code&gt; field (0-10 scale) maps to a reputation label: scores of 7 or above are &quot;High&quot;, 4-6 are &quot;Medium&quot;, and below 4 are &quot;Low&quot;. This scoring considers factors like organization age, repository portfolio, community size, and contributor count. It helps the LLM choose authoritative sources over potentially unreliable forks or clones.&lt;/p&gt;
&lt;h4 id=&quot;endpoint-2-documentation-retrieval&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#endpoint-2-documentation-retrieval&quot; aria-label=&quot;endpoint 2 documentation retrieval permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Endpoint 2: Documentation retrieval&lt;/h4&gt;
&lt;p&gt;This endpoint powers the &lt;code class=&quot;language-text&quot;&gt;query-docs&lt;/code&gt; tool. It performs vector similarity search, applies server-side reranking, and returns formatted documentation snippets.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;URL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;https://context7.com/api/v2/context&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Method&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authentication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Optional (Bearer token with &lt;code class=&quot;language-text&quot;&gt;ctx7sk_&lt;/code&gt; prefix)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Response Format&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Text (default for MCP) or JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Query Parameters:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;query&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;The developer&apos;s question or task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;libraryId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Context7-compatible library ID from &lt;code class=&quot;language-text&quot;&gt;resolve-library-id&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Response Headers:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Header&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;x-context7-total-tokens&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Total token count in response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;x-context7-total-pages&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Total number of pages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;x-context7-has-next&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whether more pages exist&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The response contains formatted documentation snippets with code examples, each including a title, description, source URL, and language-tagged code block - ready for the LLM to incorporate into its answer.&lt;/p&gt;
&lt;h3 id=&quot;request-headers-and-authentication&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#request-headers-and-authentication&quot; aria-label=&quot;request headers and authentication permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Request headers and authentication&lt;/h3&gt;
&lt;p&gt;Every request from the MCP server to Context7&apos;s API includes a set of headers that serve both operational and security purposes:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Header&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;X-Context7-Source&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Identifies the request source (&lt;code class=&quot;language-text&quot;&gt;mcp-server&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;X-Context7-Server-Version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;MCP server version for compatibility tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;Authorization&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;Bearer ctx7sk_xxx&lt;/code&gt; for authenticated access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;mcp-client-ip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;AES-256-CBC encrypted client IP for rate limiting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;X-Context7-Client-IDE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Which IDE is making the request (e.g., &lt;code class=&quot;language-text&quot;&gt;claude-code&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Cursor&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;X-Context7-Transport&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Transport type (&lt;code class=&quot;language-text&quot;&gt;stdio&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;http&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;sse&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The encrypted client IP header deserves special mention. Context7 uses AES-256-CBC encryption to prevent header spoofing - a technique where unauthorized users could bypass rate limiting by sending a different IP address with each request. The encryption key is shared only between the MCP server and the API backend, so forged headers are immediately detectable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Further reading:&lt;/strong&gt; Rate limiting in agentic workflows introduces unique challenges - AI agents can generate bursts of requests that look nothing like human traffic. We explore cost-aware rate limiting strategies for such scenarios in &lt;a href=&quot;https://handsonarchitects.com/blog/2025/denial-of-wallet-cost-aware-rate-limiting-part-1/&quot;&gt;Denial of Wallet - Cost-Aware Rate Limiting&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-request-flow-in-action&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-request-flow-in-action&quot; aria-label=&quot;the request flow in action permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The request flow in action&lt;/h3&gt;
&lt;p&gt;Consider a complete request flow for a developer asking: &quot;How to create a REST API in Spring Boot 3.5?&quot;&lt;/p&gt;
&lt;p&gt;&lt;svg id=&quot;mermaid-2&quot; width=&quot;100%&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; style=&quot;max-width: 1221px;&quot; viewBox=&quot;-50 -10 1221 752&quot; role=&quot;graphics-document document&quot; aria-roledescription=&quot;sequence&quot;&gt;&lt;g&gt;&lt;rect x=&quot;971&quot; y=&quot;666&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;API&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-bottom&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;1046&quot; y=&quot;698.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;1046&quot; dy=&quot;0&quot;&gt;Context7 API&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x=&quot;728&quot; y=&quot;666&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;MCP&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-bottom&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;803&quot; y=&quot;698.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;803&quot; dy=&quot;0&quot;&gt;MCP Server&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x=&quot;434&quot; y=&quot;666&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;LLM&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-bottom&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;509&quot; y=&quot;698.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;509&quot; dy=&quot;0&quot;&gt;LLM Client&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x=&quot;0&quot; y=&quot;666&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;DEV&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-bottom&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;75&quot; y=&quot;698.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;75&quot; dy=&quot;0&quot;&gt;Developer&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;line id=&quot;actor3&quot; x1=&quot;1046&quot; y1=&quot;65&quot; x2=&quot;1046&quot; y2=&quot;666&quot; class=&quot;actor-line 200&quot; stroke-width=&quot;0.5px&quot; stroke=&quot;#999&quot; name=&quot;API&quot; data-et=&quot;life-line&quot; data-id=&quot;API&quot;&gt;&lt;/line&gt;&lt;g id=&quot;root-3&quot; data-et=&quot;participant&quot; datatype=&quot;participant&quot; data-id=&quot;API&quot;&gt;&lt;rect x=&quot;971&quot; y=&quot;0&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;API&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-top&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;1046&quot; y=&quot;32.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;1046&quot; dy=&quot;0&quot;&gt;Context7 API&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id=&quot;actor2&quot; x1=&quot;803&quot; y1=&quot;65&quot; x2=&quot;803&quot; y2=&quot;666&quot; class=&quot;actor-line 200&quot; stroke-width=&quot;0.5px&quot; stroke=&quot;#999&quot; name=&quot;MCP&quot; data-et=&quot;life-line&quot; data-id=&quot;MCP&quot;&gt;&lt;/line&gt;&lt;g id=&quot;root-2&quot; data-et=&quot;participant&quot; datatype=&quot;participant&quot; data-id=&quot;MCP&quot;&gt;&lt;rect x=&quot;728&quot; y=&quot;0&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;MCP&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-top&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;803&quot; y=&quot;32.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;803&quot; dy=&quot;0&quot;&gt;MCP Server&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id=&quot;actor1&quot; x1=&quot;509&quot; y1=&quot;65&quot; x2=&quot;509&quot; y2=&quot;666&quot; class=&quot;actor-line 200&quot; stroke-width=&quot;0.5px&quot; stroke=&quot;#999&quot; name=&quot;LLM&quot; data-et=&quot;life-line&quot; data-id=&quot;LLM&quot;&gt;&lt;/line&gt;&lt;g id=&quot;root-1&quot; data-et=&quot;participant&quot; datatype=&quot;participant&quot; data-id=&quot;LLM&quot;&gt;&lt;rect x=&quot;434&quot; y=&quot;0&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;LLM&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-top&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;509&quot; y=&quot;32.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;509&quot; dy=&quot;0&quot;&gt;LLM Client&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id=&quot;actor0&quot; x1=&quot;75&quot; y1=&quot;65&quot; x2=&quot;75&quot; y2=&quot;666&quot; class=&quot;actor-line 200&quot; stroke-width=&quot;0.5px&quot; stroke=&quot;#999&quot; name=&quot;DEV&quot; data-et=&quot;life-line&quot; data-id=&quot;DEV&quot;&gt;&lt;/line&gt;&lt;g id=&quot;root-0&quot; data-et=&quot;participant&quot; datatype=&quot;participant&quot; data-id=&quot;DEV&quot;&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;DEV&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-top&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;75&quot; y=&quot;32.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;75&quot; dy=&quot;0&quot;&gt;Developer&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;style&gt;#mermaid-2{font-family:arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-2 .error-icon{fill:#552222;}#mermaid-2 .error-text{fill:#552222;stroke:#552222;}#mermaid-2 .edge-thickness-normal{stroke-width:1px;}#mermaid-2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-2 .marker{fill:#333333;stroke:#333333;}#mermaid-2 .marker.cross{stroke:#333333;}#mermaid-2 svg{font-family:arial,sans-serif;font-size:16px;}#mermaid-2 p{margin:0;}#mermaid-2 .actor{stroke:#9370DB;fill:#ECECFF;stroke-width:1;}#mermaid-2 rect.actor.outer-path[data-look=&quot;neo&quot;]{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-2 rect.note[data-look=&quot;neo&quot;]{stroke:#aaaa33;fill:#fff5ad;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-2 text.actor&gt;tspan{fill:black;stroke:none;}#mermaid-2 .actor-line{stroke:#9370DB;}#mermaid-2 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-2 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-2 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-2 [id$=&quot;-arrowhead&quot;] path{fill:#333;stroke:#333;}#mermaid-2 .sequenceNumber{fill:white;}#mermaid-2 [id$=&quot;-sequencenumber&quot;]{fill:#333;}#mermaid-2 [id$=&quot;-crosshead&quot;] path{fill:#333;stroke:#333;}#mermaid-2 .messageText{fill:#333;stroke:none;}#mermaid-2 .labelBox{stroke:#9370DB;fill:#ECECFF;filter:none;}#mermaid-2 .labelText,#mermaid-2 .labelText&gt;tspan{fill:black;stroke:none;}#mermaid-2 .loopText,#mermaid-2 .loopText&gt;tspan{fill:black;stroke:none;}#mermaid-2 .sectionTitle,#mermaid-2 .sectionTitle&gt;tspan{fill:black;stroke:none;}#mermaid-2 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:#9370DB;fill:#9370DB;}#mermaid-2 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-2 .noteText,#mermaid-2 .noteText&gt;tspan{fill:black;stroke:none;font-weight:normal;}#mermaid-2 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-2 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-2 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-2 .actorPopupMenu{position:absolute;}#mermaid-2 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-2 .actor-man circle,#mermaid-2 line{fill:#ECECFF;stroke-width:2px;}#mermaid-2 g rect.rect{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));stroke:#9370DB;}#mermaid-2 .node .neo-node{stroke:#9370DB;}#mermaid-2 [data-look=&quot;neo&quot;].node rect,#mermaid-2 [data-look=&quot;neo&quot;].cluster rect,#mermaid-2 [data-look=&quot;neo&quot;].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-2 [data-look=&quot;neo&quot;].swimlane.cluster rect{filter:none;}#mermaid-2 [data-look=&quot;neo&quot;].node path{stroke:#9370DB;stroke-width:1px;}#mermaid-2 [data-look=&quot;neo&quot;].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-2 [data-look=&quot;neo&quot;].node .neo-line path{stroke:#9370DB;filter:none;}#mermaid-2 [data-look=&quot;neo&quot;].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-2 [data-look=&quot;neo&quot;].node circle .state-start{fill:#000000;}#mermaid-2 [data-look=&quot;neo&quot;].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-2 [data-look=&quot;neo&quot;].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-2 :root{--mermaid-font-family:arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;/g&gt;&lt;defs&gt;&lt;symbol id=&quot;mermaid-2-computer&quot; width=&quot;24&quot; height=&quot;24&quot;&gt;&lt;path transform=&quot;scale(.5)&quot; d=&quot;M2 2v13h20v-13h-20zm18 11h-16v-9h16v9zm-10.228 6l.466-1h3.524l.467 1h-4.457zm14.228 3h-24l2-6h2.104l-1.33 4h18.45l-1.297-4h2.073l2 6zm-5-10h-14v-7h14v7z&quot;&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;symbol id=&quot;mermaid-2-database&quot; fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot;&gt;&lt;path transform=&quot;scale(.5)&quot; d=&quot;M12.258.001l.256.004.255.005.253.008.251.01.249.012.247.015.246.016.242.019.241.02.239.023.236.024.233.027.231.028.229.031.225.032.223.034.22.036.217.038.214.04.211.041.208.043.205.045.201.046.198.048.194.05.191.051.187.053.183.054.18.056.175.057.172.059.168.06.163.061.16.063.155.064.15.066.074.033.073.033.071.034.07.034.069.035.068.035.067.035.066.035.064.036.064.036.062.036.06.036.06.037.058.037.058.037.055.038.055.038.053.038.052.038.051.039.05.039.048.039.047.039.045.04.044.04.043.04.041.04.04.041.039.041.037.041.036.041.034.041.033.042.032.042.03.042.029.042.027.042.026.043.024.043.023.043.021.043.02.043.018.044.017.043.015.044.013.044.012.044.011.045.009.044.007.045.006.045.004.045.002.045.001.045v17l-.001.045-.002.045-.004.045-.006.045-.007.045-.009.044-.011.045-.012.044-.013.044-.015.044-.017.043-.018.044-.02.043-.021.043-.023.043-.024.043-.026.043-.027.042-.029.042-.03.042-.032.042-.033.042-.034.041-.036.041-.037.041-.039.041-.04.041-.041.04-.043.04-.044.04-.045.04-.047.039-.048.039-.05.039-.051.039-.052.038-.053.038-.055.038-.055.038-.058.037-.058.037-.06.037-.06.036-.062.036-.064.036-.064.036-.066.035-.067.035-.068.035-.069.035-.07.034-.071.034-.073.033-.074.033-.15.066-.155.064-.16.063-.163.061-.168.06-.172.059-.175.057-.18.056-.183.054-.187.053-.191.051-.194.05-.198.048-.201.046-.205.045-.208.043-.211.041-.214.04-.217.038-.22.036-.223.034-.225.032-.229.031-.231.028-.233.027-.236.024-.239.023-.241.02-.242.019-.246.016-.247.015-.249.012-.251.01-.253.008-.255.005-.256.004-.258.001-.258-.001-.256-.004-.255-.005-.253-.008-.251-.01-.249-.012-.247-.015-.245-.016-.243-.019-.241-.02-.238-.023-.236-.024-.234-.027-.231-.028-.228-.031-.226-.032-.223-.034-.22-.036-.217-.038-.214-.04-.211-.041-.208-.043-.204-.045-.201-.046-.198-.048-.195-.05-.19-.051-.187-.053-.184-.054-.179-.056-.176-.057-.172-.059-.167-.06-.164-.061-.159-.063-.155-.064-.151-.066-.074-.033-.072-.033-.072-.034-.07-.034-.069-.035-.068-.035-.067-.035-.066-.035-.064-.036-.063-.036-.062-.036-.061-.036-.06-.037-.058-.037-.057-.037-.056-.038-.055-.038-.053-.038-.052-.038-.051-.039-.049-.039-.049-.039-.046-.039-.046-.04-.044-.04-.043-.04-.041-.04-.04-.041-.039-.041-.037-.041-.036-.041-.034-.041-.033-.042-.032-.042-.03-.042-.029-.042-.027-.042-.026-.043-.024-.043-.023-.043-.021-.043-.02-.043-.018-.044-.017-.043-.015-.044-.013-.044-.012-.044-.011-.045-.009-.044-.007-.045-.006-.045-.004-.045-.002-.045-.001-.045v-17l.001-.045.002-.045.004-.045.006-.045.007-.045.009-.044.011-.045.012-.044.013-.044.015-.044.017-.043.018-.044.02-.043.021-.043.023-.043.024-.043.026-.043.027-.042.029-.042.03-.042.032-.042.033-.042.034-.041.036-.041.037-.041.039-.041.04-.041.041-.04.043-.04.044-.04.046-.04.046-.039.049-.039.049-.039.051-.039.052-.038.053-.038.055-.038.056-.038.057-.037.058-.037.06-.037.061-.036.062-.036.063-.036.064-.036.066-.035.067-.035.068-.035.069-.035.07-.034.072-.034.072-.033.074-.033.151-.066.155-.064.159-.063.164-.061.167-.06.172-.059.176-.057.179-.056.184-.054.187-.053.19-.051.195-.05.198-.048.201-.046.204-.045.208-.043.211-.041.214-.04.217-.038.22-.036.223-.034.226-.032.228-.031.231-.028.234-.027.236-.024.238-.023.241-.02.243-.019.245-.016.247-.015.249-.012.251-.01.253-.008.255-.005.256-.004.258-.001.258.001zm-9.258 20.499v.01l.001.021.003.021.004.022.005.021.006.022.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.023.018.024.019.024.021.024.022.025.023.024.024.025.052.049.056.05.061.051.066.051.07.051.075.051.079.052.084.052.088.052.092.052.097.052.102.051.105.052.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.048.144.049.147.047.152.047.155.047.16.045.163.045.167.043.171.043.176.041.178.041.183.039.187.039.19.037.194.035.197.035.202.033.204.031.209.03.212.029.216.027.219.025.222.024.226.021.23.02.233.018.236.016.24.015.243.012.246.01.249.008.253.005.256.004.259.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.021.224-.024.22-.026.216-.027.212-.028.21-.031.205-.031.202-.034.198-.034.194-.036.191-.037.187-.039.183-.04.179-.04.175-.042.172-.043.168-.044.163-.045.16-.046.155-.046.152-.047.148-.048.143-.049.139-.049.136-.05.131-.05.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.053.083-.051.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.05.023-.024.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.023.01-.022.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.127l-.077.055-.08.053-.083.054-.085.053-.087.052-.09.052-.093.051-.095.05-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.045-.118.044-.12.043-.122.042-.124.042-.126.041-.128.04-.13.04-.132.038-.134.038-.135.037-.138.037-.139.035-.142.035-.143.034-.144.033-.147.032-.148.031-.15.03-.151.03-.153.029-.154.027-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.01-.179.008-.179.008-.181.006-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.006-.179-.008-.179-.008-.178-.01-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.027-.153-.029-.151-.03-.15-.03-.148-.031-.146-.032-.145-.033-.143-.034-.141-.035-.14-.035-.137-.037-.136-.037-.134-.038-.132-.038-.13-.04-.128-.04-.126-.041-.124-.042-.122-.042-.12-.044-.117-.043-.116-.045-.113-.045-.112-.046-.109-.047-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.05-.093-.052-.09-.051-.087-.052-.085-.053-.083-.054-.08-.054-.077-.054v4.127zm0-5.654v.011l.001.021.003.021.004.021.005.022.006.022.007.022.009.022.01.022.011.023.012.023.013.023.015.024.016.023.017.024.018.024.019.024.021.024.022.024.023.025.024.024.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.052.11.051.114.051.119.052.123.05.127.051.131.05.135.049.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.044.171.042.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.022.23.02.233.018.236.016.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.012.241-.015.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.048.139-.05.136-.049.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.051.051-.049.023-.025.023-.024.021-.025.02-.024.019-.024.018-.024.017-.024.015-.023.014-.023.013-.024.012-.022.01-.023.01-.023.008-.022.006-.022.006-.022.004-.021.004-.022.001-.021.001-.021v-4.139l-.077.054-.08.054-.083.054-.085.052-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.044-.118.044-.12.044-.122.042-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.035-.143.033-.144.033-.147.033-.148.031-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.009-.179.009-.179.007-.181.007-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.007-.179-.007-.179-.009-.178-.009-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.031-.146-.033-.145-.033-.143-.033-.141-.035-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.04-.126-.041-.124-.042-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.051-.093-.051-.09-.051-.087-.053-.085-.052-.083-.054-.08-.054-.077-.054v4.139zm0-5.666v.011l.001.02.003.022.004.021.005.022.006.021.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.024.018.023.019.024.021.025.022.024.023.024.024.025.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.051.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.043.171.043.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.021.23.02.233.018.236.017.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.013.241-.014.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.049.139-.049.136-.049.131-.051.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.049.023-.025.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.022.01-.023.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.153l-.077.054-.08.054-.083.053-.085.053-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.048-.105.048-.106.048-.109.046-.111.046-.114.046-.115.044-.118.044-.12.043-.122.043-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.034-.143.034-.144.033-.147.032-.148.032-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.024-.161.024-.162.023-.163.023-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.01-.178.01-.179.009-.179.007-.181.006-.182.006-.182.004-.184.003-.184.001-.185.001-.185-.001-.184-.001-.184-.003-.182-.004-.182-.006-.181-.006-.179-.007-.179-.009-.178-.01-.176-.01-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.023-.162-.023-.161-.024-.159-.024-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.032-.146-.032-.145-.033-.143-.034-.141-.034-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.041-.126-.041-.124-.041-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.048-.105-.048-.102-.048-.1-.05-.097-.049-.095-.051-.093-.051-.09-.052-.087-.052-.085-.053-.083-.053-.08-.054-.077-.054v4.153zm8.74-8.179l-.257.004-.254.005-.25.008-.247.011-.244.012-.241.014-.237.016-.233.018-.231.021-.226.022-.224.023-.22.026-.216.027-.212.028-.21.031-.205.032-.202.033-.198.034-.194.036-.191.038-.187.038-.183.04-.179.041-.175.042-.172.043-.168.043-.163.045-.16.046-.155.046-.152.048-.148.048-.143.048-.139.049-.136.05-.131.05-.126.051-.123.051-.118.051-.114.052-.11.052-.106.052-.101.052-.096.052-.092.052-.088.052-.083.052-.079.052-.074.051-.07.052-.065.051-.06.05-.056.05-.051.05-.023.025-.023.024-.021.024-.02.025-.019.024-.018.024-.017.023-.015.024-.014.023-.013.023-.012.023-.01.023-.01.022-.008.022-.006.023-.006.021-.004.022-.004.021-.001.021-.001.021.001.021.001.021.004.021.004.022.006.021.006.023.008.022.01.022.01.023.012.023.013.023.014.023.015.024.017.023.018.024.019.024.02.025.021.024.023.024.023.025.051.05.056.05.06.05.065.051.07.052.074.051.079.052.083.052.088.052.092.052.096.052.101.052.106.052.11.052.114.052.118.051.123.051.126.051.131.05.136.05.139.049.143.048.148.048.152.048.155.046.16.046.163.045.168.043.172.043.175.042.179.041.183.04.187.038.191.038.194.036.198.034.202.033.205.032.21.031.212.028.216.027.22.026.224.023.226.022.231.021.233.018.237.016.241.014.244.012.247.011.25.008.254.005.257.004.26.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.022.224-.023.22-.026.216-.027.212-.028.21-.031.205-.032.202-.033.198-.034.194-.036.191-.038.187-.038.183-.04.179-.041.175-.042.172-.043.168-.043.163-.045.16-.046.155-.046.152-.048.148-.048.143-.048.139-.049.136-.05.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.05.051-.05.023-.025.023-.024.021-.024.02-.025.019-.024.018-.024.017-.023.015-.024.014-.023.013-.023.012-.023.01-.023.01-.022.008-.022.006-.023.006-.021.004-.022.004-.021.001-.021.001-.021-.001-.021-.001-.021-.004-.021-.004-.022-.006-.021-.006-.023-.008-.022-.01-.022-.01-.023-.012-.023-.013-.023-.014-.023-.015-.024-.017-.023-.018-.024-.019-.024-.02-.025-.021-.024-.023-.024-.023-.025-.051-.05-.056-.05-.06-.05-.065-.051-.07-.052-.074-.051-.079-.052-.083-.052-.088-.052-.092-.052-.096-.052-.101-.052-.106-.052-.11-.052-.114-.052-.118-.051-.123-.051-.126-.051-.131-.05-.136-.05-.139-.049-.143-.048-.148-.048-.152-.048-.155-.046-.16-.046-.163-.045-.168-.043-.172-.043-.175-.042-.179-.041-.183-.04-.187-.038-.191-.038-.194-.036-.198-.034-.202-.033-.205-.032-.21-.031-.212-.028-.216-.027-.22-.026-.224-.023-.226-.022-.231-.021-.233-.018-.237-.016-.241-.014-.244-.012-.247-.011-.25-.008-.254-.005-.257-.004-.26-.001-.26.001z&quot;&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;symbol id=&quot;mermaid-2-clock&quot; width=&quot;24&quot; height=&quot;24&quot;&gt;&lt;path transform=&quot;scale(.5)&quot; d=&quot;M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5.848 12.459c.202.038.202.333.001.372-1.907.361-6.045 1.111-6.547 1.111-.719 0-1.301-.582-1.301-1.301 0-.512.77-5.447 1.125-7.445.034-.192.312-.181.343.014l.985 6.238 5.394 1.011z&quot;&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id=&quot;mermaid-2-arrowhead&quot; refX=&quot;7.9&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto-start-reverse&quot;&gt;&lt;path d=&quot;M -1 0 L 10 5 L 0 10 z&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id=&quot;mermaid-2-crosshead&quot; markerWidth=&quot;15&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot; refX=&quot;4&quot; refY=&quot;4.5&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;#000000&quot; stroke-width=&quot;1pt&quot; d=&quot;M 1,2 L 6,7 M 6,2 L 1,7&quot; style=&quot;stroke-dasharray: 0, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id=&quot;mermaid-2-filled-head&quot; refX=&quot;15.5&quot; refY=&quot;7&quot; markerWidth=&quot;20&quot; markerHeight=&quot;28&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 18,7 L9,13 L14,7 L9,1 Z&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id=&quot;mermaid-2-sequencenumber&quot; refX=&quot;15&quot; refY=&quot;15&quot; markerWidth=&quot;60&quot; markerHeight=&quot;40&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;15&quot; cy=&quot;15&quot; r=&quot;6&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id=&quot;mermaid-2-solidTopArrowHead&quot; refX=&quot;7.9&quot; refY=&quot;7.25&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto-start-reverse&quot;&gt;&lt;path d=&quot;M 0 0 L 10 8 L 0 8 z&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id=&quot;mermaid-2-solidBottomArrowHead&quot; refX=&quot;7.9&quot; refY=&quot;0.75&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto-start-reverse&quot;&gt;&lt;path d=&quot;M 0 0 L 10 0 L 0 8 z&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id=&quot;mermaid-2-stickTopArrowHead&quot; refX=&quot;7.5&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto-start-reverse&quot;&gt;&lt;path d=&quot;M 0 0 L 7 7&quot; stroke=&quot;black&quot; stroke-width=&quot;1.5&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id=&quot;mermaid-2-stickBottomArrowHead&quot; refX=&quot;7.5&quot; refY=&quot;0&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto-start-reverse&quot;&gt;&lt;path d=&quot;M 0 7 L 7 0&quot; stroke=&quot;black&quot; stroke-width=&quot;1.5&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;g data-et=&quot;note&quot; data-id=&quot;i1&quot;&gt;&lt;rect x=&quot;484&quot; y=&quot;119&quot; fill=&quot;#EDF2AE&quot; stroke=&quot;#666&quot; width=&quot;587&quot; height=&quot;37&quot; class=&quot;note&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;778&quot; y=&quot;124&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;noteText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;&lt;tspan x=&quot;778&quot;&gt;Step 1 - Library Resolution&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x=&quot;798&quot; y=&quot;200&quot; fill=&quot;#EDF2AE&quot; stroke=&quot;#666&quot; width=&quot;10&quot; height=&quot;132&quot; class=&quot;activation0&quot;&gt;&lt;/rect&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x=&quot;1041&quot; y=&quot;246&quot; fill=&quot;#EDF2AE&quot; stroke=&quot;#666&quot; width=&quot;10&quot; height=&quot;42&quot; class=&quot;activation0&quot;&gt;&lt;/rect&gt;&lt;/g&gt;&lt;g data-et=&quot;note&quot; data-id=&quot;i10&quot;&gt;&lt;rect x=&quot;310&quot; y=&quot;342&quot; fill=&quot;#EDF2AE&quot; stroke=&quot;#666&quot; width=&quot;398&quot; height=&quot;37&quot; class=&quot;note&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;509&quot; y=&quot;347&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;noteText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;&lt;tspan x=&quot;509&quot;&gt;Select best match: /spring-projects/spring-boot/v3.5.9&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g data-et=&quot;note&quot; data-id=&quot;i11&quot;&gt;&lt;rect x=&quot;484&quot; y=&quot;389&quot; fill=&quot;#EDF2AE&quot; stroke=&quot;#666&quot; width=&quot;587&quot; height=&quot;37&quot; class=&quot;note&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;778&quot; y=&quot;394&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;noteText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;&lt;tspan x=&quot;778&quot;&gt;Step 2 - Documentation Retrieval&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x=&quot;798&quot; y=&quot;470&quot; fill=&quot;#EDF2AE&quot; stroke=&quot;#666&quot; width=&quot;10&quot; height=&quot;132&quot; class=&quot;activation0&quot;&gt;&lt;/rect&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x=&quot;1041&quot; y=&quot;516&quot; fill=&quot;#EDF2AE&quot; stroke=&quot;#666&quot; width=&quot;10&quot; height=&quot;42&quot; class=&quot;activation0&quot;&gt;&lt;/rect&gt;&lt;/g&gt;&lt;text x=&quot;291&quot; y=&quot;80&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;&quot;How to create a REST API in Spring Boot 3.5?&quot;&lt;/text&gt;&lt;line x1=&quot;76&quot; y1=&quot;109&quot; x2=&quot;505&quot; y2=&quot;109&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i0&quot; data-from=&quot;DEV&quot; data-to=&quot;LLM&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-2-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;655&quot; y=&quot;171&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;resolve-library-id(&quot;Spring Boot&quot;)&lt;/text&gt;&lt;line x1=&quot;510&quot; y1=&quot;200&quot; x2=&quot;799&quot; y2=&quot;200&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i2&quot; data-from=&quot;LLM&quot; data-to=&quot;MCP&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-2-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;925&quot; y=&quot;215&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;GET /v2/libs/search&lt;/text&gt;&lt;line x1=&quot;808&quot; y1=&quot;244&quot; x2=&quot;1042&quot; y2=&quot;244&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i4&quot; data-from=&quot;MCP&quot; data-to=&quot;API&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-2-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;926&quot; y=&quot;259&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;5 matching libraries&lt;/text&gt;&lt;line x1=&quot;1041&quot; y1=&quot;288&quot; x2=&quot;811&quot; y2=&quot;288&quot; class=&quot;messageLine1&quot; data-et=&quot;message&quot; data-id=&quot;i6&quot; data-from=&quot;API&quot; data-to=&quot;MCP&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-2-arrowhead)&quot; style=&quot;stroke-dasharray: 3, 3; fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;656&quot; y=&quot;303&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;Library list with metadata&lt;/text&gt;&lt;line x1=&quot;798&quot; y1=&quot;332&quot; x2=&quot;513&quot; y2=&quot;332&quot; class=&quot;messageLine1&quot; data-et=&quot;message&quot; data-id=&quot;i8&quot; data-from=&quot;MCP&quot; data-to=&quot;LLM&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-2-arrowhead)&quot; style=&quot;stroke-dasharray: 3, 3; fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;655&quot; y=&quot;441&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;query-docs(libraryId, query)&lt;/text&gt;&lt;line x1=&quot;510&quot; y1=&quot;470&quot; x2=&quot;799&quot; y2=&quot;470&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i12&quot; data-from=&quot;LLM&quot; data-to=&quot;MCP&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-2-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;925&quot; y=&quot;485&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;GET /v2/context&lt;/text&gt;&lt;line x1=&quot;808&quot; y1=&quot;514&quot; x2=&quot;1042&quot; y2=&quot;514&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i14&quot; data-from=&quot;MCP&quot; data-to=&quot;API&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-2-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;926&quot; y=&quot;529&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;5 relevant code snippets&lt;/text&gt;&lt;line x1=&quot;1041&quot; y1=&quot;558&quot; x2=&quot;811&quot; y2=&quot;558&quot; class=&quot;messageLine1&quot; data-et=&quot;message&quot; data-id=&quot;i16&quot; data-from=&quot;API&quot; data-to=&quot;MCP&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-2-arrowhead)&quot; style=&quot;stroke-dasharray: 3, 3; fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;656&quot; y=&quot;573&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;Formatted documentation&lt;/text&gt;&lt;line x1=&quot;798&quot; y1=&quot;602&quot; x2=&quot;513&quot; y2=&quot;602&quot; class=&quot;messageLine1&quot; data-et=&quot;message&quot; data-id=&quot;i18&quot; data-from=&quot;MCP&quot; data-to=&quot;LLM&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-2-arrowhead)&quot; style=&quot;stroke-dasharray: 3, 3; fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;294&quot; y=&quot;617&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;Generated code using current Spring Boot 3.5 APIs&lt;/text&gt;&lt;line x1=&quot;508&quot; y1=&quot;646&quot; x2=&quot;79&quot; y2=&quot;646&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i20&quot; data-from=&quot;LLM&quot; data-to=&quot;DEV&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-2-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 1: Library Resolution.&lt;/strong&gt; The LLM calls &lt;code class=&quot;language-text&quot;&gt;resolve-library-id&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;libraryName: &quot;Spring Boot&quot;&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;query: &quot;How to create a REST API in Spring Boot 3.5?&quot;&lt;/code&gt;. The MCP server translates this into a &lt;code class=&quot;language-text&quot;&gt;GET /v2/libs/search&lt;/code&gt; request. The API returns five matching libraries, including the official Spring Boot repository (&lt;code class=&quot;language-text&quot;&gt;/spring-projects/spring-boot&lt;/code&gt;) with a benchmark score of 82.5 and &quot;High&quot; source reputation.&lt;/p&gt;
&lt;p&gt;The LLM evaluates the candidates and selects &lt;code class=&quot;language-text&quot;&gt;/spring-projects/spring-boot/v3.5.9&lt;/code&gt; - choosing the latest 3.5.x version that matches the developer&apos;s request.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 2: Documentation Retrieval.&lt;/strong&gt; The LLM calls &lt;code class=&quot;language-text&quot;&gt;query-docs&lt;/code&gt; with the selected library ID and the original query. The MCP server translates this into a &lt;code class=&quot;language-text&quot;&gt;GET /v2/context&lt;/code&gt; request. The API performs vector similarity search, applies server-side reranking, and returns five relevant code snippets demonstrating REST API creation in Spring Boot. These snippets include a complete create, read, update, delete (CRUD) example with &lt;code class=&quot;language-text&quot;&gt;@RestController&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@RequestMapping&lt;/code&gt;, proper HTTP status codes, and error handling.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 3: Code Generation.&lt;/strong&gt; The LLM combines the retrieved documentation with the original prompt and any existing code context to generate a response using current Spring Boot 3.5 APIs.&lt;/p&gt;
&lt;p&gt;The entire workflow completed with just &lt;strong&gt;2 MCP tool calls&lt;/strong&gt; - the minimum possible for a query where the library name needs resolution.&lt;/p&gt;
&lt;h2 id=&quot;performance-and-quality-in-practice&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#performance-and-quality-in-practice&quot; aria-label=&quot;performance and quality in practice permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Performance and quality in practice&lt;/h2&gt;
&lt;p&gt;Performance metrics and quality experiments tell us whether Context7&apos;s architecture delivers on its promises. This section presents measurements of API response times, caching behavior, scalability, and response quality across multiple programming languages.&lt;/p&gt;
&lt;h3 id=&quot;api-performance-analysis&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#api-performance-analysis&quot; aria-label=&quot;api performance analysis permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;API performance analysis&lt;/h3&gt;
&lt;p&gt;To understand how Context7 performs under real-world conditions, we conducted a series of experiments measuring API response times, caching behavior, and scalability under concurrent load. All measurements were made using direct HTTP calls to the Context7 API endpoints.&lt;/p&gt;
&lt;h4 id=&quot;caching-behavior&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#caching-behavior&quot; aria-label=&quot;caching behavior permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Caching behavior&lt;/h4&gt;
&lt;p&gt;We called each endpoint four times with the same query to observe caching effects:&lt;/p&gt;
&lt;div style=&quot;display: flex; gap: 1.5rem; flex-wrap: wrap;&quot;&gt;
  &lt;div style=&quot;flex: 1; min-width: 280px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;/v2/libs/search&lt;/code&gt; - Library Resolution:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Call&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Response Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Cold (first)&lt;/td&gt;
&lt;td&gt;1.321s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Warm (repeat)&lt;/td&gt;
&lt;td&gt;1.377s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Warm (repeat)&lt;/td&gt;
&lt;td&gt;1.085s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Warm (repeat)&lt;/td&gt;
&lt;td&gt;1.301s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Average&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.271s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
  &lt;/div&gt;
  &lt;div style=&quot;flex: 1; min-width: 280px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;/v2/context&lt;/code&gt; - Documentation Retrieval:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Call&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Response Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Cold (first)&lt;/td&gt;
&lt;td&gt;1.183s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Warm (repeat)&lt;/td&gt;
&lt;td&gt;0.950s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Warm (repeat)&lt;/td&gt;
&lt;td&gt;0.968s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Warm (repeat)&lt;/td&gt;
&lt;td&gt;1.282s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Average&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.096s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Response times remain consistent across repeated queries (1.08 to 1.38s range for search, 0.95 to 1.28s for context). The identical response sizes across warm calls suggest server-side caching is active. However, the LLM-based relevance ranking - which carries 80% weight in the reranking algorithm - must run on every request, even for cached results, preventing dramatic cache-hit speedups.&lt;/p&gt;
&lt;h4 id=&quot;concurrent-user-simulation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#concurrent-user-simulation&quot; aria-label=&quot;concurrent user simulation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Concurrent user simulation&lt;/h4&gt;
&lt;p&gt;To test scalability, we fired 10 parallel unique queries at each endpoint, simulating 10 active users querying different libraries simultaneously:&lt;/p&gt;
&lt;div style=&quot;display: flex; gap: 1.5rem; flex-wrap: wrap;&quot;&gt;
&lt;div style=&quot;flex: 1; min-width: 280px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;/v2/libs/search&lt;/code&gt; - 10 Parallel Queries:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Response Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Spring Boot&lt;/td&gt;
&lt;td&gt;0.978s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes&lt;/td&gt;
&lt;td&gt;1.364s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Express&lt;/td&gt;
&lt;td&gt;1.482s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next.js&lt;/td&gt;
&lt;td&gt;1.498s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React&lt;/td&gt;
&lt;td&gt;1.513s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MongoDB&lt;/td&gt;
&lt;td&gt;1.568s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;1.688s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Django&lt;/td&gt;
&lt;td&gt;1.892s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vue.js&lt;/td&gt;
&lt;td&gt;1.896s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;2.053s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Average&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.593s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div style=&quot;flex: 1; min-width: 280px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;/v2/context&lt;/code&gt; - 10 Parallel Queries:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Response Time&lt;/th&gt;
&lt;th&gt;Response Size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Prisma&lt;/td&gt;
&lt;td&gt;1.272s&lt;/td&gt;
&lt;td&gt;3,716 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next.js&lt;/td&gt;
&lt;td&gt;1.278s&lt;/td&gt;
&lt;td&gt;4,124 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React&lt;/td&gt;
&lt;td&gt;1.369s&lt;/td&gt;
&lt;td&gt;5,119 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MongoDB&lt;/td&gt;
&lt;td&gt;1.371s&lt;/td&gt;
&lt;td&gt;5,224 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Express&lt;/td&gt;
&lt;td&gt;1.391s&lt;/td&gt;
&lt;td&gt;5,840 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes&lt;/td&gt;
&lt;td&gt;1.407s&lt;/td&gt;
&lt;td&gt;10,595 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Django&lt;/td&gt;
&lt;td&gt;1.585s&lt;/td&gt;
&lt;td&gt;3,122 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vue.js&lt;/td&gt;
&lt;td&gt;1.588s&lt;/td&gt;
&lt;td&gt;8,131 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;1.667s&lt;/td&gt;
&lt;td&gt;4,695 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spring Boot&lt;/td&gt;
&lt;td&gt;1.686s&lt;/td&gt;
&lt;td&gt;6,325 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Average&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.461s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The results show that Context7 handles concurrent queries with only a modest increase in response time - averaging around 1.5 seconds for both endpoints under 10 parallel requests. This suggests that the system&apos;s architecture effectively manages load without significant degradation in performance.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 900px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAABhUlEQVR42q2SaU/CQBRF+f//SIUIHwxLB8XElEVBpLK0tLQdqtDZrq+VYKsS/OAkJ50tZ96dTgWFZg7fdbLDOo6RJBwRjxFsObi/QegHCGkuiEJaS/BbqxitwJMUD09hjj0OUX8I0bID9AZrjMY+EMUwjgu98CDcANIPoaT8XbjbUzVLElzYOU2CXdmwLrNxn/p9jBoD2I0hVt0xMHkluUdxPvMYY74SUr8ipYDvRWCNpyMdwirQIprVIe7aL4hl+Xp+CDVF9t0I1vXoJIzoXg/RZAs4XOe600KjEXgxrPrjaTLxzQSt2TumXJ2pkITnKrTqRHWANpvDDjUKjp/CfbrPhewQ7SS1AVh3AbYS8BL5D8KMLH5tiFvmYJMCQplC+OIdunG++U9k4gnHzVzATQ73aQrC7H9lwg6d/CfoXVrPW3RWEk6sIPW3CkEVbvgOdy/JkdvptjTuzd5w73ySzy1T9DyB9iLF7PAwzVFITWtdOqU81hAihVQCUqZQSpbWTGGvUgofpvDD61jnV6wAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Horizontal bar chart comparing response times for 10 concurrent library queries across search and documentation retrieval endpoints, all completing within 0.98 to 2.05 seconds&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/b20234415e1f441bf84e81503569dc12/1cfc2/concurrent-response-times.png&quot;
        srcset=&quot;/static/b20234415e1f441bf84e81503569dc12/ff46a/concurrent-response-times.png 325w,
/static/b20234415e1f441bf84e81503569dc12/a6d36/concurrent-response-times.png 650w,
/static/b20234415e1f441bf84e81503569dc12/1cfc2/concurrent-response-times.png 900w&quot;
        sizes=&quot;(max-width: 900px) 100vw, 900px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACRElEQVR42mNgQAH/GUFk5a3/orv+/5f6//+/wKP//4U2/f8v8uzoVfnHF2/J7Aeyr/3/L/nw4UNBBmzg/6pVzPVdJyVKuk74g3Bp53G/+O4LGcndpxKLOg9FNnbs8/6aXiX93ytT839Mpc6lnB6Nh5ntShMnbmMH62dgYISb9R/ooInbtvHNad9gOLt+ywQYXtiwuXte/Zae2Q1b+xfUbercUL2mYnndhurjhXNj/seWG/5P6ND9D/UN2BBkA1etWsU5rXuzzoym7Y0wPLN5RxMIzwKxm7Y1T23Z0Tq9eUfXlNbdhULb/vEhBw+Kb0EG1u/fzzK7a6PerMatLbjwbKChcxu2Nld1H8+zX/RSBaobh4H/65nmtG7WmdW4rQkXBhk4vWlHtdPcx54GS1+p4nch0MCZHet1ZzVuacKJm7Y2zq/f1FbfcTBHYcUX/fr6/0xYIxhk4JT9+3lmd23Tm9OwvRGnK4FhObdxS1tz16FMs0WvnbynP5JGT2pIkbKfZ2bvVt2ZQI2E8faGmQ3bWrva9+caz/wkErrqPxuGC4EE05zunTqzGrY2EsIwg+OnXg3QWvTWw2zxYxnkCIInoZkdW3VnN27pwI63YuDIqTcC9Je8dwTFONCVzGgurGcqmnpQNmzadXcYjphxwwPGDp1yxSN08jXXoGk3XUA4euYtN9sFz+wMF76wNFn4wgWejIBhCfItmAlM3Mz/kZy9H5g2kWxlmnlmE1f9/PkcvcBMsG0bJMuBQH19PdP+/fVwtaDsCADu/8gzfeVLQgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Horizontal bar chart comparing response times for 10 concurrent library queries across search and documentation retrieval endpoints, all completing within 0.98 to 2.05 seconds&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/ea59e388a9439534c4ded2f05cb76105/3c492/concurrent-response-times-dark.png&quot;
        srcset=&quot;/static/ea59e388a9439534c4ded2f05cb76105/ff46a/concurrent-response-times-dark.png 325w,
/static/ea59e388a9439534c4ded2f05cb76105/a6d36/concurrent-response-times-dark.png 650w,
/static/ea59e388a9439534c4ded2f05cb76105/3c492/concurrent-response-times-dark.png 1300w,
/static/ea59e388a9439534c4ded2f05cb76105/d61c2/concurrent-response-times-dark.png 1800w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Notably, response size doesn&apos;t strongly correlate with response time - Kubernetes returned the largest response (10.6 KB) but wasn&apos;t the slowest, suggesting the bottleneck is server-side processing (e.g., 5-step ranking algorithm) rather than data transfer.&lt;/p&gt;
&lt;h4 id=&quot;full-end-to-end-workflow&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#full-end-to-end-workflow&quot; aria-label=&quot;full end to end workflow permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Full end-to-end workflow&lt;/h4&gt;
&lt;p&gt;A complete workflow (resolve library + fetch documentation) takes:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workflow&lt;/th&gt;
&lt;th&gt;Library Resolution&lt;/th&gt;
&lt;th&gt;Documentation Retrieval&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Spring Boot REST API&lt;/td&gt;
&lt;td&gt;1.174s&lt;/td&gt;
&lt;td&gt;1.512s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.717s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React Hooks&lt;/td&gt;
&lt;td&gt;1.459s&lt;/td&gt;
&lt;td&gt;0.978s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.471s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next.js Routing&lt;/td&gt;
&lt;td&gt;1.095s&lt;/td&gt;
&lt;td&gt;1.181s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.309s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Average&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.499s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A developer can expect approximately &lt;strong&gt;2.3 to 2.7 seconds&lt;/strong&gt; for a complete Context7 query when the library name needs resolution. If the library ID is provided directly (skipping resolution), the time drops to just the documentation retrieval phase - roughly 1 second.&lt;/p&gt;
&lt;h3 id=&quot;api-key-vs-anonymous-access&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#api-key-vs-anonymous-access&quot; aria-label=&quot;api key vs anonymous access permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;API key vs. anonymous access&lt;/h3&gt;
&lt;p&gt;Context7 offers both authenticated (API key) and anonymous access. Their documentation states that API keys provide &quot;higher rate limits, priority access during peak usage, and better performance for frequent queries.&quot; We tested whether this holds true in practice.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Without API Key&lt;/th&gt;
&lt;th&gt;With API Key&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Search API (single user avg)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.41s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.38s&lt;/td&gt;
&lt;td&gt;API key ~2% faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context API (single user avg)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.14s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.38s&lt;/td&gt;
&lt;td&gt;Anonymous 17% faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search API (10 concurrent avg)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.58s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.75s&lt;/td&gt;
&lt;td&gt;Anonymous 10% faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context API (10 concurrent avg)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.40s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.68s&lt;/td&gt;
&lt;td&gt;Anonymous 17% faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full workflow (avg)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.32s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2.81s&lt;/td&gt;
&lt;td&gt;Anonymous 17% faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response time consistency&lt;/td&gt;
&lt;td&gt;Higher variance&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Lower variance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;API key more consistent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The results were counterintuitive. Anonymous requests were generally &lt;strong&gt;faster&lt;/strong&gt; in our tests, while API key requests showed more &lt;strong&gt;consistent&lt;/strong&gt; response times with lower variance. This suggests that API key validation adds a small overhead to each request, and the &quot;priority access&quot; benefit likely applies during actual peak usage periods rather than the normal load conditions of our experiment.&lt;/p&gt;
&lt;p&gt;The primary value of an API key isn&apos;t raw speed but rather &lt;strong&gt;higher rate limits&lt;/strong&gt; (free plan: 1,000 API calls/month; pro plan: 5,000/seat/month) and &lt;strong&gt;more predictable behavior&lt;/strong&gt; under heavy load. For production use with frequent queries, an API key is essential to avoid rate limits.&lt;/p&gt;
&lt;h3 id=&quot;response-quality-experiments-across-languages-and-topics&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#response-quality-experiments-across-languages-and-topics&quot; aria-label=&quot;response quality experiments across languages and topics permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Response quality: Experiments across languages and topics&lt;/h3&gt;
&lt;p&gt;Performance metrics tell us how fast Context7 responds, but they say nothing about the quality of what it returns. To evaluate this, we conducted 12 experiments across three programming languages (Java, TypeScript, Python) and four topics (JWT Authentication, MCP Server Implementation, Database Access, Unit Testing).&lt;/p&gt;
&lt;p&gt;Each experiment was scored on accuracy, version specificity, code quality, completeness, and relevance, yielding an overall score out of 10.&lt;/p&gt;
&lt;h4 id=&quot;results-by-experiment&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#results-by-experiment&quot; aria-label=&quot;results by experiment permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Results by experiment&lt;/h4&gt;
&lt;p&gt;The following table shows individual scores for each language-topic combination. Scores of 8.0+ indicate production-ready responses; scores below 6.0 signal that the queried library lacked sufficient coverage for the topic.&lt;/p&gt;
&lt;div class=&quot;table-wide&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Library Used&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;JWT Authentication&lt;/td&gt;
&lt;td&gt;Spring Security 6.5&lt;/td&gt;
&lt;td&gt;8.2&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;JWT Authentication&lt;/td&gt;
&lt;td&gt;Express.js 5.1.0&lt;/td&gt;
&lt;td&gt;3.5&lt;/td&gt;
&lt;td&gt;Poor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;JWT Authentication&lt;/td&gt;
&lt;td&gt;FastAPI&lt;/td&gt;
&lt;td&gt;9.2&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;MCP Server&lt;/td&gt;
&lt;td&gt;Java SDK&lt;/td&gt;
&lt;td&gt;9.5&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;MCP Server&lt;/td&gt;
&lt;td&gt;TypeScript SDK&lt;/td&gt;
&lt;td&gt;9.3&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;MCP Server&lt;/td&gt;
&lt;td&gt;Python SDK&lt;/td&gt;
&lt;td&gt;9.4&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;Database Access&lt;/td&gt;
&lt;td&gt;Spring Data JPA&lt;/td&gt;
&lt;td&gt;8.0&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;Database Access&lt;/td&gt;
&lt;td&gt;Prisma ORM&lt;/td&gt;
&lt;td&gt;9.0&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;Database Access&lt;/td&gt;
&lt;td&gt;SQLAlchemy 2.0&lt;/td&gt;
&lt;td&gt;8.8&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;Unit Testing&lt;/td&gt;
&lt;td&gt;JUnit 5&lt;/td&gt;
&lt;td&gt;6.5&lt;/td&gt;
&lt;td&gt;Fair&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;Unit Testing&lt;/td&gt;
&lt;td&gt;Vitest&lt;/td&gt;
&lt;td&gt;9.0&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;Unit Testing&lt;/td&gt;
&lt;td&gt;pytest&lt;/td&gt;
&lt;td&gt;8.5&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Most experiments scored in the &quot;good to excellent&quot; range (8.0+), with the notable exception of TypeScript JWT Authentication (3.5) - a clear outlier that highlights Context7&apos;s limitations when the queried functionality lives in separate libraries rather than the main framework.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.46153846153847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAACSklEQVR42o2TW08TQRSA+6d9MkLgwaDCgzEiEIGSCPhgwGi0GkShIEgsUumFS2lpK5S2tLvbe7s7s59n22LAaHSSzUxmZ77zzZw5Ptd18Vq/+9WKTRejLQNlou0iylZcdtpcNJu4SvF70wLwWD4lP7XWXFFL7TYdbRMvKcI5E5op4uUCjU4HR9a27DZaOf3lbv/zAvSMfK6raTmafKMXdS51yp5V5FUSpra+4f6YYDgSpdBq9YKWV0lkXhPIGz0zLfvOn3FYiJBp2vg8csKwubtd6waZS6XYNQq8S8t4JyzAKUbjsS5wYl9zHF8mkpjncfK8bym2Z5PMRlYIXJR6wKOyw+BmrXuPfg9oFgikZPxlT4CT3I/1DO991USiL4knF3h6mqPa1gSzDchOsXjwkQ8FA5/drJMwFUOfq7jaxZ/8O3AspIkJMHqywEwmR9pS3Ppk4KYnu8CVQlmArWvAfxjeAKZzZCuKgQ3zJlCulWPDYWjTAnMX/3FYgKX/Bt4J/gF4VFYMb5Qh9ZC52Bq7lkEgeROYl/c3FlICXCYmwFkBZiyHgeC1I+fLV0lRDAYFmB5nNrZOSIBvxXBm23s2TxjxgGL4QAyj0SUxnGfaA4rh7XXPcIL5+Crv833DEzny6I6BTk/z/HCL75bJagYWQ/uoMz+PDg8oCnA8rDiIv5Esv2Ahe8FZ1WFk20Rl5lg6CrJWlCx7mVCSjFrboVErSd/EkTlbiseq1WlUZU6qxKuDijxcyypRrRrUOzZefVVlX71ySaVRw3ZdfgIHK2KP8OLaMwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Grouped bar chart showing quality scores across 4 topics and 3 languages: MCP Server scores are consistently high at 9.3 to 9.5, while TypeScript JWT Authentication is a clear outlier at 3.5&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/113545439781477d3ad5b565f90454ca/5a190/quality-scores-by-experiment.png&quot;
        srcset=&quot;/static/113545439781477d3ad5b565f90454ca/ff46a/quality-scores-by-experiment.png 325w,
/static/113545439781477d3ad5b565f90454ca/a6d36/quality-scores-by-experiment.png 650w,
/static/113545439781477d3ad5b565f90454ca/5a190/quality-scores-by-experiment.png 800w&quot;
        sizes=&quot;(max-width: 800px) 100vw, 800px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.46153846153847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsTAAALEwEAmpwYAAADOklEQVR42oXTfUgTYRgA8NPKov7wI5A+1JyGUVRKVrZplpUabk6nnbMiiIT+if7qwwnVGeYSgsrQ6tzmPm46vdRpjdrYfa1t5daZZhIhRJQu0qK/+qMV8nR3lhQEPfDCvbwPv+d5eO9FSJJchAHEYhjEAkCMuBAhsi2f1yIYJEFQngzhklQSnYhTeb1p5W73ev4kvkTM+TNfMoSFYCQZJ6LIr1AHAmuujdlW5HZ+3XaC5JXwUpN9iusvrvfy8a2Tk0sx1pkA/AI43wSLLQZAJBgBDItV4fzy7Y7pVHFfxnD6mqCzdBUO6NlBDwXjGryMYe5XsGy6lD9afpwL6dpy6BeHxT3LmpfBqEZvenKzLs3zWiah+bap7O22Tx3it4rj9GjQpUy+Dep6p3sIRg/dqWCo7iqaXpdm/H4uyF22M2GdcxfNt0gFJtA4GCtvu+C3eOX08DEJVNhncnYQs1bhOEbJMFe1wSFVcjtU1js9LhirxtUMTWqFDjNN0RaG0d/jwjoyj37WWEK+Tzpgf1sFzzV3LvktYpEa5A2Jx+fav2zZaftoARIWqVi2BR12KVfjUKkbEsDxeVAcOb0jeoVl9b1sqKEvjw1dKLR+2Jhvi7jmRmqNjQGLU06Fa5FRszkh1/5RAsVuf3f4LzDDGL3i+w3SoYsF1tmsfCLSM8fXGjG/eVACxdhpmsnOt0cIGDmqruMGjeVP3AdXd4Dmf6CCmN5QQETIvzoUY7Ply9ZC+1Q38DU9DT7iwd7HzP4UAawf9DyAF9V31QzVK4IyU7SJY5t7mJDunjhygWM2S2GL9MKI1nDJ3+lUMGHtAriHeE/O8aj13OMu1z4/VbwWh+rzTs8jGKsyqFm6D/X5ZOnGqJ4TLkW45YFdzDBWRHzaIID9c7y2EwuY78vpZ9KvhOSYPm/aQ7zDf4zUtut8XURpgNstgEoB7I4+R28IIxu0Pl+qzPCtgaKaO5lwg1lOPT1T5JjJVFinjFFe29oYMFvl1HCl9HwQjF2svD2e6PPckmGe/uRcnl+CnJ5c2uRwp3pd1zPq3O4koW5MpfXVSseANavPZco44vInIsKjQA0TSfTD1syWfiJlE0nG/QTe3fNwqwUf9wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Grouped bar chart showing quality scores across 4 topics and 3 languages: MCP Server scores are consistently high at 9.3 to 9.5, while TypeScript JWT Authentication is a clear outlier at 3.5&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/dece57c10d9d10dbbd25c0dd08dfd987/3c492/quality-scores-by-experiment-dark.png&quot;
        srcset=&quot;/static/dece57c10d9d10dbbd25c0dd08dfd987/ff46a/quality-scores-by-experiment-dark.png 325w,
/static/dece57c10d9d10dbbd25c0dd08dfd987/a6d36/quality-scores-by-experiment-dark.png 650w,
/static/dece57c10d9d10dbbd25c0dd08dfd987/3c492/quality-scores-by-experiment-dark.png 1300w,
/static/dece57c10d9d10dbbd25c0dd08dfd987/29007/quality-scores-by-experiment-dark.png 1600w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h4 id=&quot;key-quality-observations&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#key-quality-observations&quot; aria-label=&quot;key quality observations permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Key quality observations&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;What works well:&lt;/strong&gt; Context7 excels when a single library contains comprehensive documentation for the queried topic. MCP Server implementation scored 9.4 on average because each SDK&apos;s documentation directly covers the topic. Python consistently scored highest (8.98 average) because Python frameworks like FastAPI, SQLAlchemy, and pytest tend to have well-structured, example-rich documentation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Where it struggles:&lt;/strong&gt; Context7 performs best when the queried functionality is documented within a single library. The lowest score (3.5) came from asking Express.js about JWT authentication - but JWT functionality lives in separate libraries like &lt;code class=&quot;language-text&quot;&gt;jsonwebtoken&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;passport-jwt&lt;/code&gt;, not in Express core. Similarly, the JUnit 5 experiment (6.5) missed Mockito and Spring Boot test integration because those capabilities live in separate libraries.&lt;/p&gt;
&lt;p&gt;The lesson: &lt;strong&gt;search for the specific library that implements the feature&lt;/strong&gt;, not the framework that hosts it. Decompose cross-library queries into separate searches - for example, search for &quot;jsonwebtoken JWT implementation&quot; and &quot;Express middleware integration&quot; separately. This reflects Context7&apos;s design principle of indexing libraries, not conceptual knowledge graphs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Overall quality:&lt;/strong&gt; 50% of experiments scored 9.0 or above, 75% scored 8.0 or above, and only one experiment scored below 6.0. The average across all 12 experiments was &lt;strong&gt;8.16 out of 10&lt;/strong&gt; - a strong result that positions Context7 in the &quot;good to excellent&quot; range, while revealing that it isn&apos;t a universal solution for cross-library integration questions.&lt;/p&gt;
&lt;h2 id=&quot;five-key-factors-that-make-context7-successful&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#five-key-factors-that-make-context7-successful&quot; aria-label=&quot;five key factors that make context7 successful permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Five key factors that make Context7 successful&lt;/h2&gt;
&lt;p&gt;Having dissected Context7&apos;s architecture, protocol implementation, and performance characteristics, we can now identify the factors that separate it from the thousands of MCP servers that never gain traction.&lt;/p&gt;
&lt;h3 id=&quot;1-it-solves-a-critical-pain-point&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-it-solves-a-critical-pain-point&quot; aria-label=&quot;1 it solves a critical pain point permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. It solves a critical pain point&lt;/h3&gt;
&lt;p&gt;Every developer using an AI coding assistant encounters hallucinated APIs, outdated patterns, and version conflicts. Context7 doesn&apos;t require changing your workflow - it makes your existing tools produce better output by giving them access to current documentation. It solves one problem well and accepts the trade-off: cross-library questions require multiple searches, but single-library queries are fast and accurate.&lt;/p&gt;
&lt;h3 id=&quot;2-sophisticated-architecture-behind-a-simple-interface&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-sophisticated-architecture-behind-a-simple-interface&quot; aria-label=&quot;2 sophisticated architecture behind a simple interface permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Sophisticated architecture behind a simple interface&lt;/h3&gt;
&lt;p&gt;Context7&apos;s two-tool interface hides a five-stage quality pipeline (parse, enrich, vectorize, rerank, cache) detailed in the Architecture behind Context7 section. Each stage contributes to the result quality that users experience. The key architectural bets - DiskANN for cost-effective vector storage, server-side reranking to reduce token consumption, and multi-region caching for low-latency reads - allow the system to serve 33,000+ libraries economically while maintaining sub-3-second response times and an 8.16/10 quality score across our experiments.&lt;/p&gt;
&lt;h3 id=&quot;3-zero-setup-deployment&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-zero-setup-deployment&quot; aria-label=&quot;3 zero setup deployment permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Zero-setup deployment&lt;/h3&gt;
&lt;p&gt;For most developers, setup is a single line in their MCP client&apos;s configuration file (for example, &lt;code class=&quot;language-text&quot;&gt;.cursor/mcp.json&lt;/code&gt; for Cursor or &lt;code class=&quot;language-text&quot;&gt;claude_desktop_config.json&lt;/code&gt; for Claude Desktop):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;mcpServers&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;context7&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-y&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@upstash/context7-mcp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This zero-friction onboarding is critical - every additional setup step is a point where potential users drop off.&lt;/p&gt;
&lt;h3 id=&quot;4-well-implemented-mcp-protocol&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-well-implemented-mcp-protocol&quot; aria-label=&quot;4 well implemented mcp protocol permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Well-implemented MCP protocol&lt;/h3&gt;
&lt;p&gt;As the implementation analysis showed, Context7&apos;s two tools follow a logical two-phase workflow with carefully crafted descriptions, clear parameter semantics, privacy warnings, and usage constraints. The server handles errors gracefully with actionable messages for common failure scenarios (rate limiting, invalid keys, library not found).&lt;/p&gt;
&lt;p&gt;Context7 integrates seamlessly with all major MCP-compatible editors without special configuration or workarounds - exactly what a well-implemented protocol integration should do.&lt;/p&gt;
&lt;h3 id=&quot;5-privacy-by-design&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-privacy-by-design&quot; aria-label=&quot;5 privacy by design permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. Privacy by design&lt;/h3&gt;
&lt;p&gt;In an era of growing concern about data privacy in AI tools, Context7 takes a clear architectural stance: &lt;strong&gt;your code never leaves your machine&lt;/strong&gt;. The privacy model works as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The LLM client (running locally) reformulates the developer&apos;s prompt into a documentation query&lt;/li&gt;
&lt;li&gt;Only this reformulated query - not the original code or prompt - is sent to Context7&lt;/li&gt;
&lt;li&gt;Context7 returns documentation snippets without maintaining session history or user profiles&lt;/li&gt;
&lt;li&gt;The LLM uses the documentation to generate code, all happening locally&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This stateless, query-only design means Context7 never has access to proprietary codebases, business logic, or sensitive application details. For enterprise teams evaluating MCP servers, this is often a deciding factor.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The MCP ecosystem has grown from a single protocol specification to thousands of server implementations in little over a year. But as our analysis of Context7 shows, building a successful MCP server requires more than implementing the protocol correctly. It requires solving a genuine, recurring problem; building an architecture that can deliver reliable, fast results at scale; keeping the integration surface small and focused; and respecting user privacy.&lt;/p&gt;
&lt;p&gt;For architects and technical leaders considering building their own MCP servers, Context7 offers four lessons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Solve one problem exceptionally well.&lt;/strong&gt; Focus is a strength, not a limitation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Invest in the invisible pipeline.&lt;/strong&gt; Users never see the quality infrastructure, but they experience its results.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Make adoption frictionless.&lt;/strong&gt; Every step you eliminate between discovery and value is a step that would have cost you users.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Respect user privacy.&lt;/strong&gt; Stateless, query-only designs build the trust that enterprise adoption requires.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As the MCP ecosystem matures, the servers that thrive will be those that combine strong engineering foundations with clear, focused value propositions. Context7 shows what that combination looks like in practice.&lt;/p&gt;
&lt;div class=&quot;summary-box&quot;&gt;
&lt;h2 id=&quot;tldr&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tldr&quot; aria-label=&quot;tldr permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;Context7, built by Upstash, tops the MCP server ecosystem with 44,000+ GitHub stars and 240,000+ weekly npm downloads. It feeds AI coding assistants with fresh, version-accurate documentation so they stop generating code based on stale training data. ThoughtWorks Technology Radar now lists it in its &quot;Tools&quot; section.&lt;/p&gt;
&lt;p&gt;The engineering choices behind it tell most of the story. Shifting documentation filtering from the LLM to the server reduced token usage by 65% and latency by 38%. DiskANN stores vector indexes on disk rather than in memory, so costs stay flat as the library count grows past 33,000. Multi-region Redis caching handles reads globally with low latency and eventual consistency that fits a 10-to-15-day documentation refresh cycle.&lt;/p&gt;
&lt;p&gt;The entire MCP surface consists of two tools: resolve a library name, then fetch its docs. Tool descriptions carry embedded behavioral guidance for the LLM, including privacy warnings, a three-call-per-question cap, and examples of effective queries. Setup takes one line of JSON config.&lt;/p&gt;
&lt;p&gt;Our performance tests showed full queries completing in roughly 2.5 seconds. With 10 concurrent users, response times rose only 25-33%. Anonymous access was slightly faster than API-key access in raw speed, though API keys delivered steadier response times and higher rate limits.&lt;/p&gt;
&lt;p&gt;Quality scored 8.16 out of 10 across 12 experiments spanning Java, TypeScript, and Python. Single-library topics like MCP Server implementation averaged 9.4, while cross-library tasks such as Express.js JWT authentication dropped to 3.5 because the relevant code lives in separate packages. The takeaway: target the library that owns the feature, not the framework around it.&lt;/p&gt;
&lt;p&gt;Five factors drove adoption: it addresses a pain developers face every day, it pairs deep infrastructure with a minimal interface, it requires zero setup, its tool descriptions steer LLM behavior precisely, and user code never leaves the local machine.&lt;/p&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Protecting Architecture with Automated Tests in Python]]></title><description><![CDATA[We started our engineering careers in the Java ecosystem, where we discovered Fitness Functions and then ArchUnit, which helped us govern…]]></description><link>https://handsonarchitects.com/blog/2026/protecting-architecture-with-automated-tests-in-python/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2026/protecting-architecture-with-automated-tests-in-python/</guid><pubDate>Wed, 28 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/862c83ad194e52ac3feaaeb7b5a78ee8/71c1d/architecture-tests-hero-image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAADJElEQVR42gEZA+b8ABlRkhhQkRhOkBdOjxhOjxhPjxdPkBdPkRdPkBhQkRlSlBhSlxhQkhpUmBtVmBlSlBlSlBlSlRlSlBtVlgAXT5EYUJMXT5MVT5QVTpIWTpQVT5UWUZcXUpgWVp8aVZgbUpEdXKMSTpINS5EXV58ZV54YVJoYVJkYVJkAFE6TFlKaGFadG1qfHlyhHl+lH2GoH2CnImewIFeUTjUzXjswK1SFOj9IWEY6FDtmE1OeFlKYFlOZF1SZAEGPz0iZ2Eye3Van4V2t5WOw52Wz6GOx5m/B92Z+kZxmRb2GX2hlaJ6KecKSbFRGPzh5tC53vCRrsR5hpwBas/Bit/Fou/Jxv/J2w/N6xfR+xvODzfiK2P+GwOGsiXGUaFB0ZF2hkYLZoHOsh2tmrd9bsvFMo+U/mN8AZLXubbzwd8Lyf8f1ic75j9L6kdT8grjZaGlwa252X0hCTykdl0UczXY/xIhZaHJ8QIq6XarfU6npRJzgAHnB8IrN9YvO+IzL8ZDJ64/C3prU9W9rbWgvFVs2J4dFJrxbJcVoLqpcNDlvk096l1F6k1WbymKz7kyi4wCAyPZ6r9BLY3Q/VWYxQU4gKTR2nraSqrO/knG6j3CIW0KpdVTYkmKKXEmTf259f35IdZFNia90vfFSpeQAhMv3e6/PJzE8MT1JNEJPIy44aoufo8/jwZBp2ZhlUzw6JjFEzmYxxl0vZ1hVRm6IIm6eR4Ope8HxV6jlAIfK85TK6TBBUDVEUUNTYCg0PVBoeq7t/6qRkKxBKsg2JnswMFhFQGdANiZJZzRvkzNqjDhfeXa+7lap5gCM0Pic2v08WHAjMkU8SlNMY3OBn7Gzzda0fW3PMyDgVzmGQzYGMVkRQ3YtUnYkLzccJzAiMDxnrd5YrOkAibnWk6Wwf3lbbFw2LTZEfH1+cXp/Y05DgWBJnWJJqnRUeU0+JTVLN1Z2Jkx0KDA2RlxpHCcxV5jFWK7sAGhaUYBgT518RqR8OZhuVal1UYtmTXhgUHZeTm9eUG5ZTaFxUrKCXq1/XaR6XWlSQmOAkiEsNU2DqGK18UpBZEW2G+MUAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Protecting Architecture with Automated Tests in Python&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/862c83ad194e52ac3feaaeb7b5a78ee8/3c492/architecture-tests-hero-image.png&quot;
        srcset=&quot;/static/862c83ad194e52ac3feaaeb7b5a78ee8/ff46a/architecture-tests-hero-image.png 325w,
/static/862c83ad194e52ac3feaaeb7b5a78ee8/a6d36/architecture-tests-hero-image.png 650w,
/static/862c83ad194e52ac3feaaeb7b5a78ee8/3c492/architecture-tests-hero-image.png 1300w,
/static/862c83ad194e52ac3feaaeb7b5a78ee8/71c1d/architecture-tests-hero-image.png 1536w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We started our engineering careers in the Java ecosystem, where we discovered Fitness Functions and then ArchUnit, which helped us govern our codebases more effectively. Recently, we&apos;ve been exploring Python and its ecosystem, and naturally, we wanted to find similar tools for Python. Today, we&apos;d like to share one of our findings: &lt;a href=&quot;https://github.com/zyskarch/pytestarch&quot;&gt;PyTestArch&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PyTestArch is an open source library that allows users to define architectural rules and test their code against them. It is generally inspired by ArchUnit.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There&apos;s plenty of material on why it&apos;s worth automating architectural tests, so we won&apos;t repeat it here. Instead, we&apos;ll focus on how to use PyTestArch in practice through a hands-on example.&lt;/p&gt;
&lt;h2 id=&quot;the-example-project-naan--pop-restaurant&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-example-project-naan--pop-restaurant&quot; aria-label=&quot;the example project naan  pop restaurant permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Example Project: Naan &amp;#x26; Pop Restaurant&lt;/h2&gt;
&lt;p&gt;For this demonstration, we created a small project that implements a simple restaurant ordering system using layered architecture principles. The project is based on the &quot;Naan &amp;#x26; Pop&quot; example from the &lt;a href=&quot;https://www.oreilly.com/library/view/head-first-software/9781098134341/ch06.html&quot;&gt;Head First Software Architecture&lt;/a&gt; book by Raju Gandhi, Mark Richards, and Neal Ford.&lt;/p&gt;
&lt;p&gt;The story is straightforward: Sangita created an Indian-inspired mom-and-pop restaurant called Naan &amp;#x26; Pop, specializing in flatbread sandwiches and sodas. The restaurant needs a website where customers can place orders online. Since Naan &amp;#x26; Pop is a startup with a small budget, it needs to be simple and created quickly - but also extensible enough to grow with the business.&lt;/p&gt;
&lt;p&gt;You can find the full code on &lt;a href=&quot;https://github.com/handsonarchitects/python-architecture-tests&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;understanding-the-layered-architecture&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#understanding-the-layered-architecture&quot; aria-label=&quot;understanding the layered architecture permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Understanding the Layered Architecture&lt;/h2&gt;
&lt;p&gt;The codebase is organized into three main layers, each with a specific responsibility:&lt;/p&gt;
&lt;p&gt;&lt;svg id=&quot;mermaid-0&quot; width=&quot;100%&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; class=&quot;flowchart&quot; style=&quot;max-width: 210.3125px;&quot; viewBox=&quot;0 0 210.3125 326&quot; role=&quot;graphics-document document&quot; aria-roledescription=&quot;flowchart-v2&quot;&gt;&lt;style&gt;#mermaid-0{font-family:arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-0 .error-icon{fill:#552222;}#mermaid-0 .error-text{fill:#552222;stroke:#552222;}#mermaid-0 .edge-thickness-normal{stroke-width:1px;}#mermaid-0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-0 .marker{fill:#333333;stroke:#333333;}#mermaid-0 .marker.cross{stroke:#333333;}#mermaid-0 svg{font-family:arial,sans-serif;font-size:16px;}#mermaid-0 p{margin:0;}#mermaid-0 .label{font-family:arial,sans-serif;color:#333;}#mermaid-0 .cluster-label text{fill:#333;}#mermaid-0 .cluster-label span{color:#333;}#mermaid-0 .cluster-label span p{background-color:transparent;}#mermaid-0 .label text,#mermaid-0 span{fill:#333;color:#333;}#mermaid-0 .node rect,#mermaid-0 .node circle,#mermaid-0 .node ellipse,#mermaid-0 .node polygon,#mermaid-0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-0 .rough-node .label text,#mermaid-0 .node .label text,#mermaid-0 .image-shape .label,#mermaid-0 .icon-shape .label{text-anchor:middle;}#mermaid-0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-0 .rough-node .label,#mermaid-0 .node .label,#mermaid-0 .image-shape .label,#mermaid-0 .icon-shape .label{text-align:center;}#mermaid-0 .node.clickable{cursor:pointer;}#mermaid-0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-0 .arrowheadPath{fill:#333333;}#mermaid-0 .edgePath .path{stroke:#333333;stroke-width:1px;}#mermaid-0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-0 .cluster text{fill:#333;}#mermaid-0 .cluster span{color:#333;}#mermaid-0 .node .collapsed-indicator{fill:#aaaa33;stroke:none;opacity:0.6;}#mermaid-0 .node .collapsed-separator{stroke:#aaaa33;stroke-width:0.75px;}#mermaid-0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-0 rect.text{fill:none;stroke-width:0;}#mermaid-0 .icon-shape,#mermaid-0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-0 .icon-shape p,#mermaid-0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-0 .icon-shape .label rect,#mermaid-0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-0 .node .neo-node{stroke:#9370DB;}#mermaid-0 [data-look=&quot;neo&quot;].node rect,#mermaid-0 [data-look=&quot;neo&quot;].cluster rect,#mermaid-0 [data-look=&quot;neo&quot;].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].swimlane.cluster rect{filter:none;}#mermaid-0 [data-look=&quot;neo&quot;].node path{stroke:#9370DB;stroke-width:1px;}#mermaid-0 [data-look=&quot;neo&quot;].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].node .neo-line path{stroke:#9370DB;filter:none;}#mermaid-0 [data-look=&quot;neo&quot;].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].node circle .state-start{fill:#000000;}#mermaid-0 [data-look=&quot;neo&quot;].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 :root{--mermaid-font-family:arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-pointEnd&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-pointStart&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-pointEnd-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 11.5 14&quot; refX=&quot;11.5&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;10.5&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 11.5 7 L 0 14 z&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-pointStart-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 11.5 14&quot; refX=&quot;1&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11.5&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;polygon points=&quot;0,7 11.5,14 11.5,0&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/polygon&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-circleEnd&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;11&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-circleStart&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;-1&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-circleEnd-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refY=&quot;5&quot; refX=&quot;12.25&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;14&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-circleStart-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;-2&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;14&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-crossEnd&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 11 11&quot; refX=&quot;12&quot; refY=&quot;5.2&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 l 9,9 M 10,1 l -9,9&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-crossStart&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 11 11&quot; refX=&quot;-1&quot; refY=&quot;5.2&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 l 9,9 M 10,1 l -9,9&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-crossEnd-margin&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 15 15&quot; refX=&quot;17.7&quot; refY=&quot;7.5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 L 14,14 M 1,14 L 14,1&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2.5;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-crossStart-margin&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 15 15&quot; refX=&quot;-3.5&quot; refY=&quot;7.5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 L 14,14 M 1,14 L 14,1&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2.5; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;g class=&quot;root&quot;&gt;&lt;g class=&quot;clusters&quot;&gt;&lt;/g&gt;&lt;g class=&quot;edgePaths&quot;&gt;&lt;path d=&quot;M105.156,62L105.156,68.167C105.156,74.333,105.156,86.667,105.156,98.333C105.156,110,105.156,121,105.156,126.5L105.156,132&quot; id=&quot;mermaid-0-L_Presentation_Workflow_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_Presentation_Workflow_0&quot; data-points=&quot;W3sieCI6MTA1LjE1NjI1LCJ5Ijo2Mn0seyJ4IjoxMDUuMTU2MjUsInkiOjk5fSx7IngiOjEwNS4xNTYyNSwieSI6MTM2fV0=&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M105.156,190L105.156,196.167C105.156,202.333,105.156,214.667,105.156,226.333C105.156,238,105.156,249,105.156,254.5L105.156,260&quot; id=&quot;mermaid-0-L_Workflow_Persistence_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_Workflow_Persistence_0&quot; data-points=&quot;W3sieCI6MTA1LjE1NjI1LCJ5IjoxOTB9LHsieCI6MTA1LjE1NjI1LCJ5IjoyMjd9LHsieCI6MTA1LjE1NjI1LCJ5IjoyNjR9XQ==&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabels&quot;&gt;&lt;g class=&quot;edgeLabel&quot; transform=&quot;translate(105.15625, 99)&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_Presentation_Workflow_0&quot; transform=&quot;translate(-41.8203125, -12)&quot;&gt;&lt;foreignObject width=&quot;83.640625&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;p&gt;depends on&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabel&quot; transform=&quot;translate(105.15625, 227)&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_Workflow_Persistence_0&quot; transform=&quot;translate(-41.8203125, -12)&quot;&gt;&lt;foreignObject width=&quot;83.640625&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;p&gt;depends on&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;nodes&quot;&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-Presentation-0&quot; data-look=&quot;classic&quot; transform=&quot;translate(105.15625, 35)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;stroke:#3b82f6 !important;stroke-width:2px !important&quot; x=&quot;-97.15625&quot; y=&quot;-27&quot; width=&quot;194.3125&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-67.15625, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;134.3125&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Presentation Layer&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-Workflow-1&quot; data-look=&quot;classic&quot; transform=&quot;translate(105.15625, 163)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;stroke:#e8833a !important;stroke-width:2px !important&quot; x=&quot;-84.984375&quot; y=&quot;-27&quot; width=&quot;169.96875&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-54.984375, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;109.96875&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Workflow Layer&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-Persistence-2&quot; data-look=&quot;classic&quot; transform=&quot;translate(105.15625, 291)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;stroke:#10b981 !important;stroke-width:2px !important&quot; x=&quot;-94.03125&quot; y=&quot;-27&quot; width=&quot;188.0625&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-64.03125, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;128.0625&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Persistence Layer&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;defs&gt;&lt;filter id=&quot;mermaid-0-drop-shadow&quot; height=&quot;130%&quot; width=&quot;130%&quot;&gt;&lt;feDropShadow dx=&quot;4&quot; dy=&quot;4&quot; stdDeviation=&quot;0&quot; flood-opacity=&quot;0.06&quot; flood-color=&quot;#000000&quot;&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;defs&gt;&lt;filter id=&quot;mermaid-0-drop-shadow-small&quot; height=&quot;150%&quot; width=&quot;150%&quot;&gt;&lt;feDropShadow dx=&quot;2&quot; dy=&quot;2&quot; stdDeviation=&quot;0&quot; flood-opacity=&quot;0.06&quot; flood-color=&quot;#000000&quot;&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;/svg&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Presentation Layer&lt;/strong&gt;: FastAPI routers, endpoints, and request/response models&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Workflow Layer&lt;/strong&gt;: Business logic and use case orchestration&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Persistence Layer&lt;/strong&gt;: Entity models, value objects, and data structures&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This layered architecture was chosen for Naan &amp;#x26; Pop because it delivers one of the most important characteristics for a startup: &lt;strong&gt;fast time to market&lt;/strong&gt;. Compared to more complex patterns like hexagonal or microservices architectures, it&apos;s simple to understand and implement, allowing the team to get the system running quickly without sacrificing maintainability.&lt;/p&gt;
&lt;p&gt;But speed isn&apos;t the only benefit. One of the most powerful advantages of layered architecture is the &lt;strong&gt;ease of replacing entire technical layers&lt;/strong&gt;. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Switching databases&lt;/strong&gt;: Need to move from PostgreSQL to MongoDB? You only change the persistence layer - your workflow and presentation layers remain untouched because they interact with persistence through the workflow layer, which handles data transformation between presentation DTOs and persistence entities.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adding new interfaces&lt;/strong&gt;: Want to add a mobile app alongside the web interface? Add a new presentation layer (e.g., a REST API or GraphQL endpoint) without touching your existing business logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key architectural rules make this possible:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Allowed dependencies&lt;/strong&gt;: Presentation → Workflow and Workflow → Persistence&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Forbidden dependencies&lt;/strong&gt;: Presentation → Persistence (layer skipping) and any reverse dependencies (lower layers depending on higher ones)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Why is layer skipping so problematic? It breaks this replaceability. If the presentation layer directly accesses the persistence layer, then changes to your database schema or data access patterns would require updates in &lt;strong&gt;both&lt;/strong&gt; the workflow layer and the presentation layer. For instance, renaming a database column from &lt;code class=&quot;language-text&quot;&gt;order_id&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; would force you to update not only your persistence models and workflow logic but also every presentation router that directly imports those models. You&apos;ve lost the ability to change one technical concern in isolation. Beyond that, bypassing the workflow layer means circumventing business logic and validation rules, potentially leading to data inconsistencies.&lt;/p&gt;
&lt;p&gt;These rules sound simple, but in practice they require constant vigilance. As the codebase grows, it&apos;s easy to accidentally introduce forbidden dependencies. A developer might be tempted to import a persistence entity directly into a presentation router &quot;just this once&quot; to save time, breaking the architectural integrity that makes the system maintainable. This is where automated testing becomes essential.&lt;/p&gt;
&lt;h2 id=&quot;protecting-architecture-with-pytestarch&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#protecting-architecture-with-pytestarch&quot; aria-label=&quot;protecting architecture with pytestarch permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Protecting Architecture with PyTestArch&lt;/h2&gt;
&lt;p&gt;This is where PyTestArch comes into play. We defined a set of architectural rules using PyTestArch syntax that enforce the correct dependencies between layers.&lt;/p&gt;
&lt;h3 id=&quot;setting-up-the-foundation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#setting-up-the-foundation&quot; aria-label=&quot;setting up the foundation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Setting Up the Foundation&lt;/h3&gt;
&lt;p&gt;Before we dive into the test code, PyTestArch uses &lt;a href=&quot;https://docs.pytest.org/en/7.1.x/how-to/fixtures.html&quot;&gt;pytest fixtures&lt;/a&gt; to configure the architecture. The &lt;code class=&quot;language-text&quot;&gt;evaluable&lt;/code&gt; fixture scans your codebase (scan all python files under src for imports and build an internal representation that can later be queried), and &lt;code class=&quot;language-text&quot;&gt;layered_architecture&lt;/code&gt; defines your layer structure. These are set up once in &lt;code class=&quot;language-text&quot;&gt;conftest.py&lt;/code&gt; and reused across all tests:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# tests/architecture/conftest.py (simplified)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; pytest
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; pytestarch &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; EvaluableArchitecture&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; LayeredArchitecture&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; get_evaluable_architecture

&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@pytest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fixture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;session&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;evaluable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;src_dir&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; EvaluableArchitecture&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    naan_and_pop_path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;join&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;src_dir&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;naan_and_pop&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; get_evaluable_architecture&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;src_dir&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; naan_and_pop_path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@pytest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fixture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scope&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;session&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;layered_architecture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; LayeredArchitecture&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        LayeredArchitecture&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layer&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;presentation&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;containing_modules&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;src.naan_and_pop.presentation&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layer&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;workflow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;containing_modules&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;src.naan_and_pop.workflow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layer&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;persistence&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;containing_modules&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;src.naan_and_pop.persistence&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; When defining layer modules, ensure module paths match how PyTestArch resolves imports from your project root. In this example, we use &lt;code class=&quot;language-text&quot;&gt;src.naan_and_pop.presentation&lt;/code&gt; because our source directory is &lt;code class=&quot;language-text&quot;&gt;src/&lt;/code&gt;. Your paths may differ based on your project structure.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;writing-architectural-rules&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#writing-architectural-rules&quot; aria-label=&quot;writing architectural rules permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Writing Architectural Rules&lt;/h3&gt;
&lt;p&gt;Now let&apos;s see how these fixtures are used in a test that prevents layer skipping:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; pytestarch &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; EvaluableArchitecture&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; LayerRule&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; LayeredArchitecture


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test_presentation_cannot_skip_layers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    evaluable&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; EvaluableArchitecture&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; layered_architecture&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; LayeredArchitecture
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;
    Presentation must not directly access persistence.

    All data access must go through the workflow layer to ensure business
    logic is properly encapsulated and consistently applied.

    Violations would allow bypassing business rules and validation logic.
    &quot;&quot;&quot;&lt;/span&gt;
    rule &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        LayerRule&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;based_on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;layered_architecture&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layers_that&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;are_named&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;presentation&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;should_not&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;access_layers_that&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;are_named&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;persistence&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    rule&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;assert_applies&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evaluable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;how-it-works&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-it-works&quot; aria-label=&quot;how it works permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How It Works&lt;/h3&gt;
&lt;p&gt;PyTestArch provides a fluent API for defining architectural rules. In the example above:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;LayerRule()&lt;/code&gt;&lt;/strong&gt; - Creates a rule specifically for validating layer dependencies&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;.based_on(layered_architecture)&lt;/code&gt;&lt;/strong&gt; - References our layer configuration (defined in &lt;code class=&quot;language-text&quot;&gt;conftest.py&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;.layers_that().are_named(&quot;presentation&quot;)&lt;/code&gt;&lt;/strong&gt; - Specifies which layer we&apos;re checking&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;.should_not().access_layers_that().are_named(&quot;persistence&quot;)&lt;/code&gt;&lt;/strong&gt; - Defines the forbidden dependency&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;.assert_applies(evaluable)&lt;/code&gt;&lt;/strong&gt; - Validates the rule against the actual codebase&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The test scans all imports in the presentation layer and fails if it finds any direct imports from the persistence layer. This happens automatically during your CI pipeline, catching violations before they reach production.&lt;/p&gt;
&lt;h3 id=&quot;catching-violations-in-action&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#catching-violations-in-action&quot; aria-label=&quot;catching violations in action permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Catching Violations in Action&lt;/h3&gt;
&lt;p&gt;To see PyTestArch in action, check out &lt;a href=&quot;https://github.com/handsonarchitects/python-architecture-tests/pull/1&quot;&gt;Pull Request #1&lt;/a&gt; in our demo repository. In this PR, a developer tried to import an &lt;code class=&quot;language-text&quot;&gt;Order&lt;/code&gt; entity directly from the persistence layer into a presentation model to avoid duplicating field definitions. While this seems efficient, it creates a direct coupling between the presentation and persistence layers, breaking the architectural boundary. The architecture tests catch this violation and fail the build, preventing the problematic code from being merged.&lt;/p&gt;
&lt;p&gt;When a violation occurs, PyTestArch provides clear error messages showing exactly which module violated the rule and what the forbidden import was. This makes it easy to understand and fix the issue quickly.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;E   AssertionError: &quot;src.naan_and_pop.presentation.api.models.orders&quot; (layer &quot;presentation&quot;) imports &quot;src.naan_and_pop.persistence.orders.order&quot; (layer &quot;persistence&quot;).&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This demonstrates the real value of automated architecture tests: they act as guardrails that prevent architectural erosion over time, even as team members change and the codebase grows. The example repository includes additional tests that enforce other critical rules like preventing reverse dependencies and ensuring proper layer isolation.&lt;/p&gt;
&lt;h2 id=&quot;beyond-protection-architecture-as-a-fitness-function&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#beyond-protection-architecture-as-a-fitness-function&quot; aria-label=&quot;beyond protection architecture as a fitness function permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Beyond Protection: Architecture as a Fitness Function&lt;/h2&gt;
&lt;p&gt;Architecture tests can do more than just prevent violations - they can actively guide refactoring efforts. In his video lesson &lt;a href=&quot;https://www.youtube.com/watch?v=LwxLjgK0dA8&quot;&gt;&quot;Fitness Function-Driven Architecture&quot;&lt;/a&gt;, Mark Richards discusses using ArchUnit (or similar tools in other ecosystems) as a Fitness Function that guides teams toward refactoring application architecture to a desired state.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: we highly recommend materials from Mark Richard&apos;s website &lt;a href=&quot;https://developertoarchitect.com/lessons&quot;&gt;Developer to Architect&lt;/a&gt;, which offers excellent resources on software architecture topics.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The approach is straightforward: write architecture tests for your target architecture before implementing it. These tests will initially fail, but they provide clear, measurable goals for your refactoring effort. As you restructure components and move code between layers, the tests gradually turn green, providing concrete feedback on your progress.&lt;/p&gt;
&lt;p&gt;For instance, if you&apos;re restructuring your application/service codebase to support the domain better, you might write tests that enforce new boundaries between components. As you refactor, each passing test confirms you&apos;re moving in the right direction, while failing tests highlight remaining work.&lt;/p&gt;
&lt;p&gt;This transforms architecture tests from passive guardrails into active navigation tools, helping teams systematically evolve their systems toward better architectural patterns.&lt;/p&gt;
&lt;h2 id=&quot;summary-why-use-pytestarch&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary-why-use-pytestarch&quot; aria-label=&quot;summary why use pytestarch permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary: Why Use PyTestArch?&lt;/h2&gt;
&lt;p&gt;PyTestArch brings the power of automated architecture testing to Python projects, offering several key benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prevent architectural drift&lt;/strong&gt;: Catch violations early in the development process, before they reach production&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Document architecture decisions&lt;/strong&gt;: Tests serve as executable documentation that clearly expresses architectural intent&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enable confident refactoring&lt;/strong&gt;: Know immediately if changes break architectural constraints - just like unit tests for architecture&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reduce code review burden&lt;/strong&gt;: Automate the enforcement of architectural rules that would otherwise require manual review&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Support team growth&lt;/strong&gt;: Help new team members understand and respect architectural boundaries&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;getting-started&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#getting-started&quot; aria-label=&quot;getting started permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Getting Started&lt;/h3&gt;
&lt;p&gt;Ready to try PyTestArch in your own project? Here&apos;s how to get started:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install PyTestArch: &lt;code class=&quot;language-text&quot;&gt;pip install pytestarch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Define your layers in a pytest fixture (see our example in &lt;code class=&quot;language-text&quot;&gt;tests/architecture/conftest.py&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Write your first architectural rule (start with something simple like preventing layer skipping)&lt;/li&gt;
&lt;li&gt;Run the tests and fix any violations&lt;/li&gt;
&lt;li&gt;Add the tests to your CI pipeline&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The complete example code, including all architecture tests and the full Naan &amp;#x26; Pop implementation, is available on &lt;a href=&quot;https://github.com/handsonarchitects/python-architecture-tests&quot;&gt;GitHub&lt;/a&gt;. Feel free to use it as a starting point for your own projects.&lt;/p&gt;
&lt;h3 id=&quot;when-to-use-architecture-tests&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#when-to-use-architecture-tests&quot; aria-label=&quot;when to use architecture tests permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;When to Use Architecture Tests&lt;/h3&gt;
&lt;p&gt;Architecture tests are particularly valuable for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Codebases with multiple contributors&lt;/strong&gt; to ensure everyone adheres to the same architectural principles&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Long-lived projects&lt;/strong&gt; that need to maintain architectural integrity over time&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Teams with varying experience levels&lt;/strong&gt; who benefit from automated guardrails&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Migration projects&lt;/strong&gt; where you&apos;re actively refactoring toward a target architecture&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open source projects&lt;/strong&gt; where contributors may not be familiar with architectural decisions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By investing a small amount of time upfront to define your architectural rules, you gain automated enforcement that pays dividends throughout the lifetime of your project.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Denial of Wallet: Cost-Aware Rate Limiting for Generative AI Applications - Hands-On Implementation (Part 3)]]></title><description><![CDATA[This is Part 3 of the Denial of Wallet series. Part 1 introduced Denial of Wallet as a distinct failure mode in GenAI applications. Part…]]></description><link>https://handsonarchitects.com/blog/2026/denial-of-wallet-cost-aware-rate-limiting-part-3/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2026/denial-of-wallet-cost-aware-rate-limiting-part-3/</guid><pubDate>Mon, 12 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/c1bb6970fb87d6ca2b4cd5ffa0138dff/71c1d/denial-of-wallet-3-hero.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAADEklEQVR42j2S+S+bcRzHn79gv23JYpZVRuxwzeyytmoodYUVG6OOSUQcw6pDIjWZY8RQC2MzqyGtsnURx9hWtXWuYu5Vq0IxVdZHLz2e57sHseSTbz7H651PPvm+IdS8hwViggFA5dLZ6fGhUeFni0mLmuGDEdZH93XaXb3+L8AIxHDEHwX0n3jfWEe9Yke2t8afOzn6fQAAs8mwA8C+SCQIDA6KigyvYFWtr8ksRjVqORZjBcb9GOzzu2hdRvNuTAvOjfKdnxnDmuZ9bJvpwwdOUxZ1pCIxM9yT5OurUq4ZDbDZCB+KD4lx0dcH12zbi1O63hS3VOYrlZu7m9K/OwoAjHw+r6mE/rOjjpUdd8IKNyTsL2MxJieHMBWEHGzWr8hmS2LJ9U/ie14X81l5w8LeWnoU93mmUbvNb2t4k0vr6WzOTQhlJETwu5oZBbEqpRygOggxqVGAyGXzT+ODvVzss2JCknxcG6pKu8vTWuh3tzbl0x9fMiNuF8T5T75I+FKbvw+vqP8sqTYWsHshACxLgnZRslMFBUe9YhtGwt86f4ZdmrP8qXKhtRAB6Oq8iEmjdKYFJKcmJVS8lUqmyln0bOZDYNJASzOjbWE2ovunPkacZZBw91xwdKJNMxU3UkYb49XsbCs4AwJ8KjOcFuPxqPDas9b0V5ze7rfi8c/AooH66pjjzKDmFHIfw58b6ySmWd33dHMj+d24Q3EhBWAJMbPIvbzzZgmXXM0nFbGp1TzVxhxAthEjDEkmBJLGx7schrAq9V2MK5x1Pi460g4f6EjwcyRSHNx9XEJiH2TQ0wuLAgvr3DOKvZ/WS6SzAOgOboZ1ak4ejR99iZ1IEMec/pkf1MBmX7jh5USkOBMpTqTA6wTPmiQKLzuUmxPunV0aXNI0JRbp97ZQkxpS7yhWF8Xri2Prv8Wbsl8bqxKpZMYnJOKyu7czgexwm2x3FX/TwyuTwejp6hD8GFyWzcGqNb1GifkSQi0aAAyYDQ9fA/Z7ANHuqhRy6dz87MTIsPCboF+xuoRadJjnjgHNkan/AeCAAwMf9Rj2AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Hands-on implementation of cost-aware rate limiting&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/c1bb6970fb87d6ca2b4cd5ffa0138dff/3c492/denial-of-wallet-3-hero.png&quot;
        srcset=&quot;/static/c1bb6970fb87d6ca2b4cd5ffa0138dff/ff46a/denial-of-wallet-3-hero.png 325w,
/static/c1bb6970fb87d6ca2b4cd5ffa0138dff/a6d36/denial-of-wallet-3-hero.png 650w,
/static/c1bb6970fb87d6ca2b4cd5ffa0138dff/3c492/denial-of-wallet-3-hero.png 1300w,
/static/c1bb6970fb87d6ca2b4cd5ffa0138dff/71c1d/denial-of-wallet-3-hero.png 1536w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is Part 3 of the Denial of Wallet series. &lt;a href=&quot;https://handsonarchitects.com/blog/2025/denial-of-wallet-cost-aware-rate-limiting-part-1/&quot;&gt;Part 1&lt;/a&gt; introduced Denial of Wallet as a distinct failure mode in GenAI applications. &lt;a href=&quot;https://handsonarchitects.com/blog/2025/denial-of-wallet-cost-aware-rate-limiting-part-2/&quot;&gt;Part 2&lt;/a&gt; compared rate limiting algorithms and provided a practical implementation checklist. This post delivers a hands-on implementation: a working API with multi-level rate limiting, Redis-backed quota management, real-time monitoring dashboards, and load testing scenarios that demonstrate how the system behaves under different attack patterns.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;introduction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#introduction&quot; aria-label=&quot;introduction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Introduction&lt;/h2&gt;
&lt;p&gt;As hands-on architects, we couldn&apos;t just write theoretical articles about protecting GenAI APIs from Denial of Wallet attacks. We needed to build a working example that you can run, modify, and learn from. This post walks through a complete implementation that demonstrates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Token Bucket Rate Limiter&lt;/strong&gt; - Allows controlled bursts while maintaining average rate (minute-level protection)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Daily Quota&lt;/strong&gt; - Short-term quota that prevents sustained abuse within 5-hour windows&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Weekly Quota&lt;/strong&gt; - Long-term quota that prevents extended exploitation over 7-day periods&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The implementation is built for &lt;strong&gt;educational purposes&lt;/strong&gt; using accelerated timeframes (5-minute &quot;daily&quot; and 20-minute &quot;weekly&quot; quotas) so you can observe the full behavior in under 30 minutes rather than waiting days. This architecture is inspired by &lt;a href=&quot;https://docs.anthropic.com/en/api/rate-limits&quot;&gt;Claude&apos;s current rate limits&lt;/a&gt;, which implement three levels of protection: requests per minute, 5-hour window quotas, and weekly window quotas.&lt;/p&gt;
&lt;p&gt;The full project code is available at: &lt;a href=&quot;https://github.com/handsonarchitects/denial-of-wallet-demo&quot;&gt;https://github.com/handsonarchitects/denial-of-wallet-demo&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note on Terminology&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Throughout this post, &quot;tokens&quot; refer to &lt;strong&gt;budget units&lt;/strong&gt; for rate limiting, not LLM model tokens:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1 budget unit = $0.001 (one-tenth of a cent)&lt;/li&gt;
&lt;li&gt;A $0.50 LLM request consumes 500 budget units&lt;/li&gt;
&lt;li&gt;The token bucket &quot;24 tokens&quot; means $0.024 in budget, not 24 LLM tokens&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See &lt;a href=&quot;https://handsonarchitects.com/blog/2025/denial-of-wallet-cost-aware-rate-limiting-part-2/#rate-limiting-algorithm-options-for-cost-aware-protection&quot;&gt;Part 2&lt;/a&gt; for the full terminology mapping and cost calculation formulas.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Demo Enhancements for Learning&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This implementation includes additional logging and observability features for educational clarity:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Explicit &quot;cascading limit detected&quot; messages in k6 logs&lt;/li&gt;
&lt;li&gt;Real-time dashboard with 10-second metric snapshots&lt;/li&gt;
&lt;li&gt;Accelerated quota windows (5 min &quot;daily&quot;, 20 min &quot;weekly&quot;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In production, you&apos;d replace these with your standard observability stack (e.g., Prometheus, Grafana, CloudWatch).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;application-architecture&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#application-architecture&quot; aria-label=&quot;application architecture permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Application Architecture&lt;/h2&gt;
&lt;p&gt;The API is built with &lt;strong&gt;FastAPI&lt;/strong&gt; and follows a clean architecture pattern with clear separation of concerns. Let&apos;s examine the core components and request lifecycle.&lt;/p&gt;
&lt;h3 id=&quot;core-components&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#core-components&quot; aria-label=&quot;core components permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Core Components&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Dependency Injection&lt;/strong&gt;: FastAPI&apos;s built-in DI system provides &lt;code class=&quot;language-text&quot;&gt;RateLimiter&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;RedisStorage&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;Settings&lt;/code&gt; to endpoints via &lt;code class=&quot;language-text&quot;&gt;app.state&lt;/code&gt;. This enables clean testing and makes it easy to swap implementations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Token Bucket&lt;/strong&gt;: Implements the continuous refill algorithm for smooth minute-level rate limiting. Unlike discrete interval-based approaches, tokens accumulate proportionally based on elapsed time, providing more granular burst control.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quota Manager&lt;/strong&gt;: Handles time-windowed daily and weekly quotas with automatic reset logic. Each quota tracks consumption independently and resets at configured intervals.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Metrics Recorder&lt;/strong&gt;: A background task that periodically captures state snapshots (bucket levels, quota remaining, request success/failure rates) for historical analysis and dashboard visualization. This component is done for educational purposes; in production, you&apos;d integrate with your existing monitoring solution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Storage Layer&lt;/strong&gt;: Redis-backed persistence for all state: token buckets, quotas, metrics snapshots, and request history. Redis provides atomic operations and expiration support, making it ideal for distributed rate limiting.&lt;/p&gt;
&lt;h3 id=&quot;request-lifecycle&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#request-lifecycle&quot; aria-label=&quot;request lifecycle permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Request Lifecycle&lt;/h3&gt;
&lt;p&gt;The following sequence diagram shows how a request flows through the rate limiting system. In this demo, generation time (0.1-0.5s) and actual cost (1-5 budget units) are simulated randomly to mimic real LLM variability. In production you would use the LLM proviter&apos;s usage metadata (e.g., &lt;code class=&quot;language-text&quot;&gt;prompt_tokens + completion_tokens&lt;/code&gt;) to determine actual cost after generation and use SDK tools to estimate cost beforehand.&lt;/p&gt;
&lt;p&gt;&lt;svg id=&quot;mermaid-0&quot; width=&quot;100%&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; style=&quot;max-width: 1660px;&quot; viewBox=&quot;-50 -10 1660 2469&quot; role=&quot;graphics-document document&quot; aria-roledescription=&quot;sequence&quot;&gt;&lt;g&gt;&lt;rect x=&quot;1410&quot; y=&quot;2383&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;Redis&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-bottom&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;1485&quot; y=&quot;2415.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;1485&quot; dy=&quot;0&quot;&gt;Redis&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x=&quot;1146&quot; y=&quot;2383&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;Storage&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-bottom&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;1221&quot; y=&quot;2415.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;1221&quot; dy=&quot;0&quot;&gt;RedisStorage&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x=&quot;946&quot; y=&quot;2383&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;Bucket&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-bottom&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;1021&quot; y=&quot;2415.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;1021&quot; dy=&quot;0&quot;&gt;TokenBucket&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x=&quot;746&quot; y=&quot;2383&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;Limiter&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-bottom&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;821&quot; y=&quot;2415.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;821&quot; dy=&quot;0&quot;&gt;RateLimiter&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x=&quot;546&quot; y=&quot;2383&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;Auth&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-bottom&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;621&quot; y=&quot;2415.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;621&quot; dy=&quot;0&quot;&gt;API Key Validator&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x=&quot;340&quot; y=&quot;2383&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;API&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-bottom&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;415&quot; y=&quot;2415.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;415&quot; dy=&quot;0&quot;&gt;FastAPI Endpoint&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x=&quot;0&quot; y=&quot;2383&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;Client&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-bottom&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;75&quot; y=&quot;2415.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;75&quot; dy=&quot;0&quot;&gt;Client&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;line id=&quot;actor6&quot; x1=&quot;1485&quot; y1=&quot;65&quot; x2=&quot;1485&quot; y2=&quot;2383&quot; class=&quot;actor-line 200&quot; stroke-width=&quot;0.5px&quot; stroke=&quot;#999&quot; name=&quot;Redis&quot; data-et=&quot;life-line&quot; data-id=&quot;Redis&quot;&gt;&lt;/line&gt;&lt;g id=&quot;root-6&quot; data-et=&quot;participant&quot; datatype=&quot;participant&quot; data-id=&quot;Redis&quot;&gt;&lt;rect x=&quot;1410&quot; y=&quot;0&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;Redis&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-top&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;1485&quot; y=&quot;32.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;1485&quot; dy=&quot;0&quot;&gt;Redis&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id=&quot;actor5&quot; x1=&quot;1221&quot; y1=&quot;65&quot; x2=&quot;1221&quot; y2=&quot;2383&quot; class=&quot;actor-line 200&quot; stroke-width=&quot;0.5px&quot; stroke=&quot;#999&quot; name=&quot;Storage&quot; data-et=&quot;life-line&quot; data-id=&quot;Storage&quot;&gt;&lt;/line&gt;&lt;g id=&quot;root-5&quot; data-et=&quot;participant&quot; datatype=&quot;participant&quot; data-id=&quot;Storage&quot;&gt;&lt;rect x=&quot;1146&quot; y=&quot;0&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;Storage&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-top&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;1221&quot; y=&quot;32.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;1221&quot; dy=&quot;0&quot;&gt;RedisStorage&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id=&quot;actor4&quot; x1=&quot;1021&quot; y1=&quot;65&quot; x2=&quot;1021&quot; y2=&quot;2383&quot; class=&quot;actor-line 200&quot; stroke-width=&quot;0.5px&quot; stroke=&quot;#999&quot; name=&quot;Bucket&quot; data-et=&quot;life-line&quot; data-id=&quot;Bucket&quot;&gt;&lt;/line&gt;&lt;g id=&quot;root-4&quot; data-et=&quot;participant&quot; datatype=&quot;participant&quot; data-id=&quot;Bucket&quot;&gt;&lt;rect x=&quot;946&quot; y=&quot;0&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;Bucket&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-top&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;1021&quot; y=&quot;32.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;1021&quot; dy=&quot;0&quot;&gt;TokenBucket&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id=&quot;actor3&quot; x1=&quot;821&quot; y1=&quot;65&quot; x2=&quot;821&quot; y2=&quot;2383&quot; class=&quot;actor-line 200&quot; stroke-width=&quot;0.5px&quot; stroke=&quot;#999&quot; name=&quot;Limiter&quot; data-et=&quot;life-line&quot; data-id=&quot;Limiter&quot;&gt;&lt;/line&gt;&lt;g id=&quot;root-3&quot; data-et=&quot;participant&quot; datatype=&quot;participant&quot; data-id=&quot;Limiter&quot;&gt;&lt;rect x=&quot;746&quot; y=&quot;0&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;Limiter&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-top&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;821&quot; y=&quot;32.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;821&quot; dy=&quot;0&quot;&gt;RateLimiter&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id=&quot;actor2&quot; x1=&quot;621&quot; y1=&quot;65&quot; x2=&quot;621&quot; y2=&quot;2383&quot; class=&quot;actor-line 200&quot; stroke-width=&quot;0.5px&quot; stroke=&quot;#999&quot; name=&quot;Auth&quot; data-et=&quot;life-line&quot; data-id=&quot;Auth&quot;&gt;&lt;/line&gt;&lt;g id=&quot;root-2&quot; data-et=&quot;participant&quot; datatype=&quot;participant&quot; data-id=&quot;Auth&quot;&gt;&lt;rect x=&quot;546&quot; y=&quot;0&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;Auth&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-top&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;621&quot; y=&quot;32.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;621&quot; dy=&quot;0&quot;&gt;API Key Validator&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id=&quot;actor1&quot; x1=&quot;415&quot; y1=&quot;65&quot; x2=&quot;415&quot; y2=&quot;2383&quot; class=&quot;actor-line 200&quot; stroke-width=&quot;0.5px&quot; stroke=&quot;#999&quot; name=&quot;API&quot; data-et=&quot;life-line&quot; data-id=&quot;API&quot;&gt;&lt;/line&gt;&lt;g id=&quot;root-1&quot; data-et=&quot;participant&quot; datatype=&quot;participant&quot; data-id=&quot;API&quot;&gt;&lt;rect x=&quot;340&quot; y=&quot;0&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;API&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-top&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;415&quot; y=&quot;32.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;415&quot; dy=&quot;0&quot;&gt;FastAPI Endpoint&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id=&quot;actor0&quot; x1=&quot;75&quot; y1=&quot;65&quot; x2=&quot;75&quot; y2=&quot;2383&quot; class=&quot;actor-line 200&quot; stroke-width=&quot;0.5px&quot; stroke=&quot;#999&quot; name=&quot;Client&quot; data-et=&quot;life-line&quot; data-id=&quot;Client&quot;&gt;&lt;/line&gt;&lt;g id=&quot;root-0&quot; data-et=&quot;participant&quot; datatype=&quot;participant&quot; data-id=&quot;Client&quot;&gt;&lt;rect x=&quot;0&quot; y=&quot;0&quot; fill=&quot;#eaeaea&quot; stroke=&quot;#666&quot; width=&quot;150&quot; height=&quot;65&quot; name=&quot;Client&quot; rx=&quot;3&quot; ry=&quot;3&quot; class=&quot;actor actor-top&quot;&gt;&lt;/rect&gt;&lt;text x=&quot;75&quot; y=&quot;32.5&quot; dominant-baseline=&quot;central&quot; alignment-baseline=&quot;central&quot; class=&quot;actor actor-box&quot; style=&quot;text-anchor: middle; font-size: 16px; font-weight: 400; font-family: arial, sans-serif;&quot;&gt;&lt;tspan x=&quot;75&quot; dy=&quot;0&quot;&gt;Client&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;style&gt;#mermaid-0{font-family:arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-0 .error-icon{fill:#552222;}#mermaid-0 .error-text{fill:#552222;stroke:#552222;}#mermaid-0 .edge-thickness-normal{stroke-width:1px;}#mermaid-0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-0 .marker{fill:#333333;stroke:#333333;}#mermaid-0 .marker.cross{stroke:#333333;}#mermaid-0 svg{font-family:arial,sans-serif;font-size:16px;}#mermaid-0 p{margin:0;}#mermaid-0 .actor{stroke:#9370DB;fill:#ECECFF;stroke-width:1;}#mermaid-0 rect.actor.outer-path[data-look=&quot;neo&quot;]{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 rect.note[data-look=&quot;neo&quot;]{stroke:#aaaa33;fill:#fff5ad;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 text.actor&gt;tspan{fill:black;stroke:none;}#mermaid-0 .actor-line{stroke:#9370DB;}#mermaid-0 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-0 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-0 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-0 [id$=&quot;-arrowhead&quot;] path{fill:#333;stroke:#333;}#mermaid-0 .sequenceNumber{fill:white;}#mermaid-0 [id$=&quot;-sequencenumber&quot;]{fill:#333;}#mermaid-0 [id$=&quot;-crosshead&quot;] path{fill:#333;stroke:#333;}#mermaid-0 .messageText{fill:#333;stroke:none;}#mermaid-0 .labelBox{stroke:#9370DB;fill:#ECECFF;filter:none;}#mermaid-0 .labelText,#mermaid-0 .labelText&gt;tspan{fill:black;stroke:none;}#mermaid-0 .loopText,#mermaid-0 .loopText&gt;tspan{fill:black;stroke:none;}#mermaid-0 .sectionTitle,#mermaid-0 .sectionTitle&gt;tspan{fill:black;stroke:none;}#mermaid-0 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:#9370DB;fill:#9370DB;}#mermaid-0 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-0 .noteText,#mermaid-0 .noteText&gt;tspan{fill:black;stroke:none;font-weight:normal;}#mermaid-0 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-0 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-0 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-0 .actorPopupMenu{position:absolute;}#mermaid-0 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-0 .actor-man circle,#mermaid-0 line{fill:#ECECFF;stroke-width:2px;}#mermaid-0 g rect.rect{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));stroke:#9370DB;}#mermaid-0 .node .neo-node{stroke:#9370DB;}#mermaid-0 [data-look=&quot;neo&quot;].node rect,#mermaid-0 [data-look=&quot;neo&quot;].cluster rect,#mermaid-0 [data-look=&quot;neo&quot;].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].swimlane.cluster rect{filter:none;}#mermaid-0 [data-look=&quot;neo&quot;].node path{stroke:#9370DB;stroke-width:1px;}#mermaid-0 [data-look=&quot;neo&quot;].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].node .neo-line path{stroke:#9370DB;filter:none;}#mermaid-0 [data-look=&quot;neo&quot;].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].node circle .state-start{fill:#000000;}#mermaid-0 [data-look=&quot;neo&quot;].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 :root{--mermaid-font-family:arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;/g&gt;&lt;defs&gt;&lt;symbol id=&quot;mermaid-0-computer&quot; width=&quot;24&quot; height=&quot;24&quot;&gt;&lt;path transform=&quot;scale(.5)&quot; d=&quot;M2 2v13h20v-13h-20zm18 11h-16v-9h16v9zm-10.228 6l.466-1h3.524l.467 1h-4.457zm14.228 3h-24l2-6h2.104l-1.33 4h18.45l-1.297-4h2.073l2 6zm-5-10h-14v-7h14v7z&quot;&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;symbol id=&quot;mermaid-0-database&quot; fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot;&gt;&lt;path transform=&quot;scale(.5)&quot; d=&quot;M12.258.001l.256.004.255.005.253.008.251.01.249.012.247.015.246.016.242.019.241.02.239.023.236.024.233.027.231.028.229.031.225.032.223.034.22.036.217.038.214.04.211.041.208.043.205.045.201.046.198.048.194.05.191.051.187.053.183.054.18.056.175.057.172.059.168.06.163.061.16.063.155.064.15.066.074.033.073.033.071.034.07.034.069.035.068.035.067.035.066.035.064.036.064.036.062.036.06.036.06.037.058.037.058.037.055.038.055.038.053.038.052.038.051.039.05.039.048.039.047.039.045.04.044.04.043.04.041.04.04.041.039.041.037.041.036.041.034.041.033.042.032.042.03.042.029.042.027.042.026.043.024.043.023.043.021.043.02.043.018.044.017.043.015.044.013.044.012.044.011.045.009.044.007.045.006.045.004.045.002.045.001.045v17l-.001.045-.002.045-.004.045-.006.045-.007.045-.009.044-.011.045-.012.044-.013.044-.015.044-.017.043-.018.044-.02.043-.021.043-.023.043-.024.043-.026.043-.027.042-.029.042-.03.042-.032.042-.033.042-.034.041-.036.041-.037.041-.039.041-.04.041-.041.04-.043.04-.044.04-.045.04-.047.039-.048.039-.05.039-.051.039-.052.038-.053.038-.055.038-.055.038-.058.037-.058.037-.06.037-.06.036-.062.036-.064.036-.064.036-.066.035-.067.035-.068.035-.069.035-.07.034-.071.034-.073.033-.074.033-.15.066-.155.064-.16.063-.163.061-.168.06-.172.059-.175.057-.18.056-.183.054-.187.053-.191.051-.194.05-.198.048-.201.046-.205.045-.208.043-.211.041-.214.04-.217.038-.22.036-.223.034-.225.032-.229.031-.231.028-.233.027-.236.024-.239.023-.241.02-.242.019-.246.016-.247.015-.249.012-.251.01-.253.008-.255.005-.256.004-.258.001-.258-.001-.256-.004-.255-.005-.253-.008-.251-.01-.249-.012-.247-.015-.245-.016-.243-.019-.241-.02-.238-.023-.236-.024-.234-.027-.231-.028-.228-.031-.226-.032-.223-.034-.22-.036-.217-.038-.214-.04-.211-.041-.208-.043-.204-.045-.201-.046-.198-.048-.195-.05-.19-.051-.187-.053-.184-.054-.179-.056-.176-.057-.172-.059-.167-.06-.164-.061-.159-.063-.155-.064-.151-.066-.074-.033-.072-.033-.072-.034-.07-.034-.069-.035-.068-.035-.067-.035-.066-.035-.064-.036-.063-.036-.062-.036-.061-.036-.06-.037-.058-.037-.057-.037-.056-.038-.055-.038-.053-.038-.052-.038-.051-.039-.049-.039-.049-.039-.046-.039-.046-.04-.044-.04-.043-.04-.041-.04-.04-.041-.039-.041-.037-.041-.036-.041-.034-.041-.033-.042-.032-.042-.03-.042-.029-.042-.027-.042-.026-.043-.024-.043-.023-.043-.021-.043-.02-.043-.018-.044-.017-.043-.015-.044-.013-.044-.012-.044-.011-.045-.009-.044-.007-.045-.006-.045-.004-.045-.002-.045-.001-.045v-17l.001-.045.002-.045.004-.045.006-.045.007-.045.009-.044.011-.045.012-.044.013-.044.015-.044.017-.043.018-.044.02-.043.021-.043.023-.043.024-.043.026-.043.027-.042.029-.042.03-.042.032-.042.033-.042.034-.041.036-.041.037-.041.039-.041.04-.041.041-.04.043-.04.044-.04.046-.04.046-.039.049-.039.049-.039.051-.039.052-.038.053-.038.055-.038.056-.038.057-.037.058-.037.06-.037.061-.036.062-.036.063-.036.064-.036.066-.035.067-.035.068-.035.069-.035.07-.034.072-.034.072-.033.074-.033.151-.066.155-.064.159-.063.164-.061.167-.06.172-.059.176-.057.179-.056.184-.054.187-.053.19-.051.195-.05.198-.048.201-.046.204-.045.208-.043.211-.041.214-.04.217-.038.22-.036.223-.034.226-.032.228-.031.231-.028.234-.027.236-.024.238-.023.241-.02.243-.019.245-.016.247-.015.249-.012.251-.01.253-.008.255-.005.256-.004.258-.001.258.001zm-9.258 20.499v.01l.001.021.003.021.004.022.005.021.006.022.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.023.018.024.019.024.021.024.022.025.023.024.024.025.052.049.056.05.061.051.066.051.07.051.075.051.079.052.084.052.088.052.092.052.097.052.102.051.105.052.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.048.144.049.147.047.152.047.155.047.16.045.163.045.167.043.171.043.176.041.178.041.183.039.187.039.19.037.194.035.197.035.202.033.204.031.209.03.212.029.216.027.219.025.222.024.226.021.23.02.233.018.236.016.24.015.243.012.246.01.249.008.253.005.256.004.259.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.021.224-.024.22-.026.216-.027.212-.028.21-.031.205-.031.202-.034.198-.034.194-.036.191-.037.187-.039.183-.04.179-.04.175-.042.172-.043.168-.044.163-.045.16-.046.155-.046.152-.047.148-.048.143-.049.139-.049.136-.05.131-.05.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.053.083-.051.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.05.023-.024.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.023.01-.022.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.127l-.077.055-.08.053-.083.054-.085.053-.087.052-.09.052-.093.051-.095.05-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.045-.118.044-.12.043-.122.042-.124.042-.126.041-.128.04-.13.04-.132.038-.134.038-.135.037-.138.037-.139.035-.142.035-.143.034-.144.033-.147.032-.148.031-.15.03-.151.03-.153.029-.154.027-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.01-.179.008-.179.008-.181.006-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.006-.179-.008-.179-.008-.178-.01-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.027-.153-.029-.151-.03-.15-.03-.148-.031-.146-.032-.145-.033-.143-.034-.141-.035-.14-.035-.137-.037-.136-.037-.134-.038-.132-.038-.13-.04-.128-.04-.126-.041-.124-.042-.122-.042-.12-.044-.117-.043-.116-.045-.113-.045-.112-.046-.109-.047-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.05-.093-.052-.09-.051-.087-.052-.085-.053-.083-.054-.08-.054-.077-.054v4.127zm0-5.654v.011l.001.021.003.021.004.021.005.022.006.022.007.022.009.022.01.022.011.023.012.023.013.023.015.024.016.023.017.024.018.024.019.024.021.024.022.024.023.025.024.024.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.052.11.051.114.051.119.052.123.05.127.051.131.05.135.049.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.044.171.042.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.022.23.02.233.018.236.016.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.012.241-.015.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.048.139-.05.136-.049.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.051.051-.049.023-.025.023-.024.021-.025.02-.024.019-.024.018-.024.017-.024.015-.023.014-.023.013-.024.012-.022.01-.023.01-.023.008-.022.006-.022.006-.022.004-.021.004-.022.001-.021.001-.021v-4.139l-.077.054-.08.054-.083.054-.085.052-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.044-.118.044-.12.044-.122.042-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.035-.143.033-.144.033-.147.033-.148.031-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.009-.179.009-.179.007-.181.007-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.007-.179-.007-.179-.009-.178-.009-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.031-.146-.033-.145-.033-.143-.033-.141-.035-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.04-.126-.041-.124-.042-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.051-.093-.051-.09-.051-.087-.053-.085-.052-.083-.054-.08-.054-.077-.054v4.139zm0-5.666v.011l.001.02.003.022.004.021.005.022.006.021.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.024.018.023.019.024.021.025.022.024.023.024.024.025.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.051.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.043.171.043.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.021.23.02.233.018.236.017.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.013.241-.014.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.049.139-.049.136-.049.131-.051.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.049.023-.025.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.022.01-.023.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.153l-.077.054-.08.054-.083.053-.085.053-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.048-.105.048-.106.048-.109.046-.111.046-.114.046-.115.044-.118.044-.12.043-.122.043-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.034-.143.034-.144.033-.147.032-.148.032-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.024-.161.024-.162.023-.163.023-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.01-.178.01-.179.009-.179.007-.181.006-.182.006-.182.004-.184.003-.184.001-.185.001-.185-.001-.184-.001-.184-.003-.182-.004-.182-.006-.181-.006-.179-.007-.179-.009-.178-.01-.176-.01-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.023-.162-.023-.161-.024-.159-.024-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.032-.146-.032-.145-.033-.143-.034-.141-.034-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.041-.126-.041-.124-.041-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.048-.105-.048-.102-.048-.1-.05-.097-.049-.095-.051-.093-.051-.09-.052-.087-.052-.085-.053-.083-.053-.08-.054-.077-.054v4.153zm8.74-8.179l-.257.004-.254.005-.25.008-.247.011-.244.012-.241.014-.237.016-.233.018-.231.021-.226.022-.224.023-.22.026-.216.027-.212.028-.21.031-.205.032-.202.033-.198.034-.194.036-.191.038-.187.038-.183.04-.179.041-.175.042-.172.043-.168.043-.163.045-.16.046-.155.046-.152.048-.148.048-.143.048-.139.049-.136.05-.131.05-.126.051-.123.051-.118.051-.114.052-.11.052-.106.052-.101.052-.096.052-.092.052-.088.052-.083.052-.079.052-.074.051-.07.052-.065.051-.06.05-.056.05-.051.05-.023.025-.023.024-.021.024-.02.025-.019.024-.018.024-.017.023-.015.024-.014.023-.013.023-.012.023-.01.023-.01.022-.008.022-.006.023-.006.021-.004.022-.004.021-.001.021-.001.021.001.021.001.021.004.021.004.022.006.021.006.023.008.022.01.022.01.023.012.023.013.023.014.023.015.024.017.023.018.024.019.024.02.025.021.024.023.024.023.025.051.05.056.05.06.05.065.051.07.052.074.051.079.052.083.052.088.052.092.052.096.052.101.052.106.052.11.052.114.052.118.051.123.051.126.051.131.05.136.05.139.049.143.048.148.048.152.048.155.046.16.046.163.045.168.043.172.043.175.042.179.041.183.04.187.038.191.038.194.036.198.034.202.033.205.032.21.031.212.028.216.027.22.026.224.023.226.022.231.021.233.018.237.016.241.014.244.012.247.011.25.008.254.005.257.004.26.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.022.224-.023.22-.026.216-.027.212-.028.21-.031.205-.032.202-.033.198-.034.194-.036.191-.038.187-.038.183-.04.179-.041.175-.042.172-.043.168-.043.163-.045.16-.046.155-.046.152-.048.148-.048.143-.048.139-.049.136-.05.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.05.051-.05.023-.025.023-.024.021-.024.02-.025.019-.024.018-.024.017-.023.015-.024.014-.023.013-.023.012-.023.01-.023.01-.022.008-.022.006-.023.006-.021.004-.022.004-.021.001-.021.001-.021-.001-.021-.001-.021-.004-.021-.004-.022-.006-.021-.006-.023-.008-.022-.01-.022-.01-.023-.012-.023-.013-.023-.014-.023-.015-.024-.017-.023-.018-.024-.019-.024-.02-.025-.021-.024-.023-.024-.023-.025-.051-.05-.056-.05-.06-.05-.065-.051-.07-.052-.074-.051-.079-.052-.083-.052-.088-.052-.092-.052-.096-.052-.101-.052-.106-.052-.11-.052-.114-.052-.118-.051-.123-.051-.126-.051-.131-.05-.136-.05-.139-.049-.143-.048-.148-.048-.152-.048-.155-.046-.16-.046-.163-.045-.168-.043-.172-.043-.175-.042-.179-.041-.183-.04-.187-.038-.191-.038-.194-.036-.198-.034-.202-.033-.205-.032-.21-.031-.212-.028-.216-.027-.22-.026-.224-.023-.226-.022-.231-.021-.233-.018-.237-.016-.241-.014-.244-.012-.247-.011-.25-.008-.254-.005-.257-.004-.26-.001-.26.001z&quot;&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;symbol id=&quot;mermaid-0-clock&quot; width=&quot;24&quot; height=&quot;24&quot;&gt;&lt;path transform=&quot;scale(.5)&quot; d=&quot;M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5.848 12.459c.202.038.202.333.001.372-1.907.361-6.045 1.111-6.547 1.111-.719 0-1.301-.582-1.301-1.301 0-.512.77-5.447 1.125-7.445.034-.192.312-.181.343.014l.985 6.238 5.394 1.011z&quot;&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id=&quot;mermaid-0-arrowhead&quot; refX=&quot;7.9&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto-start-reverse&quot;&gt;&lt;path d=&quot;M -1 0 L 10 5 L 0 10 z&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id=&quot;mermaid-0-crosshead&quot; markerWidth=&quot;15&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot; refX=&quot;4&quot; refY=&quot;4.5&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;#000000&quot; stroke-width=&quot;1pt&quot; d=&quot;M 1,2 L 6,7 M 6,2 L 1,7&quot; style=&quot;stroke-dasharray: 0, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id=&quot;mermaid-0-filled-head&quot; refX=&quot;15.5&quot; refY=&quot;7&quot; markerWidth=&quot;20&quot; markerHeight=&quot;28&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 18,7 L9,13 L14,7 L9,1 Z&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id=&quot;mermaid-0-sequencenumber&quot; refX=&quot;15&quot; refY=&quot;15&quot; markerWidth=&quot;60&quot; markerHeight=&quot;40&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;15&quot; cy=&quot;15&quot; r=&quot;6&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id=&quot;mermaid-0-solidTopArrowHead&quot; refX=&quot;7.9&quot; refY=&quot;7.25&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto-start-reverse&quot;&gt;&lt;path d=&quot;M 0 0 L 10 8 L 0 8 z&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id=&quot;mermaid-0-solidBottomArrowHead&quot; refX=&quot;7.9&quot; refY=&quot;0.75&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto-start-reverse&quot;&gt;&lt;path d=&quot;M 0 0 L 10 0 L 0 8 z&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id=&quot;mermaid-0-stickTopArrowHead&quot; refX=&quot;7.5&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto-start-reverse&quot;&gt;&lt;path d=&quot;M 0 0 L 7 7&quot; stroke=&quot;black&quot; stroke-width=&quot;1.5&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id=&quot;mermaid-0-stickBottomArrowHead&quot; refX=&quot;7.5&quot; refY=&quot;0&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto-start-reverse&quot;&gt;&lt;path d=&quot;M 0 7 L 7 0&quot; stroke=&quot;black&quot; stroke-width=&quot;1.5&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;g data-et=&quot;control-structure&quot; data-id=&quot;i24&quot;&gt;&lt;line x1=&quot;64&quot; y1=&quot;891&quot; x2=&quot;1232&quot; y2=&quot;891&quot; class=&quot;loopLine&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;1232&quot; y1=&quot;891&quot; x2=&quot;1232&quot; y2=&quot;1207&quot; class=&quot;loopLine&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;64&quot; y1=&quot;1207&quot; x2=&quot;1232&quot; y2=&quot;1207&quot; class=&quot;loopLine&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;64&quot; y1=&quot;891&quot; x2=&quot;64&quot; y2=&quot;1207&quot; class=&quot;loopLine&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;64&quot; y1=&quot;985&quot; x2=&quot;1232&quot; y2=&quot;985&quot; class=&quot;loopLine&quot; style=&quot;stroke-dasharray: 3, 3;&quot;&gt;&lt;/line&gt;&lt;polygon points=&quot;64,891 114,891 114,904 105.6,911 64,911&quot; class=&quot;labelBox&quot;&gt;&lt;/polygon&gt;&lt;text x=&quot;89&quot; y=&quot;904&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;labelText&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;alt&lt;/text&gt;&lt;text x=&quot;673&quot; y=&quot;909&quot; text-anchor=&quot;middle&quot; class=&quot;loopText&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;&lt;tspan x=&quot;673&quot;&gt;[Daily quota sufficient]&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;648&quot; y=&quot;1003&quot; text-anchor=&quot;middle&quot; class=&quot;sectionTitle&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;[Daily quota exhausted]&lt;/text&gt;&lt;/g&gt;&lt;g data-et=&quot;control-structure&quot; data-id=&quot;i34&quot;&gt;&lt;line x1=&quot;64&quot; y1=&quot;1307&quot; x2=&quot;1232&quot; y2=&quot;1307&quot; class=&quot;loopLine&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;1232&quot; y1=&quot;1307&quot; x2=&quot;1232&quot; y2=&quot;1623&quot; class=&quot;loopLine&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;64&quot; y1=&quot;1623&quot; x2=&quot;1232&quot; y2=&quot;1623&quot; class=&quot;loopLine&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;64&quot; y1=&quot;1307&quot; x2=&quot;64&quot; y2=&quot;1623&quot; class=&quot;loopLine&quot;&gt;&lt;/line&gt;&lt;line x1=&quot;64&quot; y1=&quot;1401&quot; x2=&quot;1232&quot; y2=&quot;1401&quot; class=&quot;loopLine&quot; style=&quot;stroke-dasharray: 3, 3;&quot;&gt;&lt;/line&gt;&lt;polygon points=&quot;64,1307 114,1307 114,1320 105.6,1327 64,1327&quot; class=&quot;labelBox&quot;&gt;&lt;/polygon&gt;&lt;text x=&quot;89&quot; y=&quot;1320&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;labelText&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;alt&lt;/text&gt;&lt;text x=&quot;673&quot; y=&quot;1325&quot; text-anchor=&quot;middle&quot; class=&quot;loopText&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;&lt;tspan x=&quot;673&quot;&gt;[Weekly quota sufficient]&lt;/tspan&gt;&lt;/text&gt;&lt;text x=&quot;648&quot; y=&quot;1419&quot; text-anchor=&quot;middle&quot; class=&quot;sectionTitle&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;[Weekly quota exhausted]&lt;/text&gt;&lt;/g&gt;&lt;text x=&quot;244&quot; y=&quot;80&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;POST /generate {prompt}&lt;/text&gt;&lt;line x1=&quot;76&quot; y1=&quot;109&quot; x2=&quot;411&quot; y2=&quot;109&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i0&quot; data-from=&quot;Client&quot; data-to=&quot;API&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;517&quot; y=&quot;124&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;Validate X-API-Key&lt;/text&gt;&lt;line x1=&quot;416&quot; y1=&quot;153&quot; x2=&quot;617&quot; y2=&quot;153&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i1&quot; data-from=&quot;API&quot; data-to=&quot;Auth&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;520&quot; y=&quot;168&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;✓ Authenticated&lt;/text&gt;&lt;line x1=&quot;620&quot; y1=&quot;197&quot; x2=&quot;419&quot; y2=&quot;197&quot; class=&quot;messageLine1&quot; data-et=&quot;message&quot; data-id=&quot;i2&quot; data-from=&quot;Auth&quot; data-to=&quot;API&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;stroke-dasharray: 3, 3; fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;416&quot; y=&quot;212&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;Estimate cost (1-5 tokens)&lt;/text&gt;&lt;path d=&quot;M 416,241 C 476,231 476,271 416,261&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i3&quot; data-from=&quot;API&quot; data-to=&quot;API&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/path&gt;&lt;text x=&quot;617&quot; y=&quot;286&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;check_and_consume_tokens(cost)&lt;/text&gt;&lt;line x1=&quot;416&quot; y1=&quot;317&quot; x2=&quot;817&quot; y2=&quot;317&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i4&quot; data-from=&quot;API&quot; data-to=&quot;Limiter&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1020&quot; y=&quot;332&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;reset_quotas_if_needed()&lt;/text&gt;&lt;line x1=&quot;822&quot; y1=&quot;363&quot; x2=&quot;1217&quot; y2=&quot;363&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i5&quot; data-from=&quot;Limiter&quot; data-to=&quot;Storage&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1352&quot; y=&quot;378&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;Check reset_at timestamps&lt;/text&gt;&lt;line x1=&quot;1222&quot; y1=&quot;409&quot; x2=&quot;1481&quot; y2=&quot;409&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i6&quot; data-from=&quot;Storage&quot; data-to=&quot;Redis&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1355&quot; y=&quot;424&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;Current times&lt;/text&gt;&lt;line x1=&quot;1484&quot; y1=&quot;453&quot; x2=&quot;1225&quot; y2=&quot;453&quot; class=&quot;messageLine1&quot; data-et=&quot;message&quot; data-id=&quot;i7&quot; data-from=&quot;Redis&quot; data-to=&quot;Storage&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;stroke-dasharray: 3, 3; fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1352&quot; y=&quot;468&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;Initialize if expired&lt;/text&gt;&lt;line x1=&quot;1222&quot; y1=&quot;497&quot; x2=&quot;1481&quot; y2=&quot;497&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i8&quot; data-from=&quot;Storage&quot; data-to=&quot;Redis&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;920&quot; y=&quot;512&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;consume(tokens)&lt;/text&gt;&lt;line x1=&quot;822&quot; y1=&quot;541&quot; x2=&quot;1017&quot; y2=&quot;541&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i9&quot; data-from=&quot;Limiter&quot; data-to=&quot;Bucket&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1252&quot; y=&quot;556&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;HGET bucket:key&lt;/text&gt;&lt;line x1=&quot;1022&quot; y1=&quot;585&quot; x2=&quot;1481&quot; y2=&quot;585&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i10&quot; data-from=&quot;Bucket&quot; data-to=&quot;Redis&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1022&quot; y=&quot;600&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;Refill based on time elapsed&lt;/text&gt;&lt;path d=&quot;M 1022,629 C 1082,619 1082,659 1022,649&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i11&quot; data-from=&quot;Bucket&quot; data-to=&quot;Bucket&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/path&gt;&lt;text x=&quot;1252&quot; y=&quot;674&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;HINCRBYFLOAT -tokens&lt;/text&gt;&lt;line x1=&quot;1022&quot; y1=&quot;703&quot; x2=&quot;1481&quot; y2=&quot;703&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i12&quot; data-from=&quot;Bucket&quot; data-to=&quot;Redis&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;923&quot; y=&quot;718&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;✓ Consumed&lt;/text&gt;&lt;line x1=&quot;1020&quot; y1=&quot;747&quot; x2=&quot;825&quot; y2=&quot;747&quot; class=&quot;messageLine1&quot; data-et=&quot;message&quot; data-id=&quot;i13&quot; data-from=&quot;Bucket&quot; data-to=&quot;Limiter&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;stroke-dasharray: 3, 3; fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1020&quot; y=&quot;762&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;get_quota(daily)&lt;/text&gt;&lt;line x1=&quot;822&quot; y1=&quot;793&quot; x2=&quot;1217&quot; y2=&quot;793&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i14&quot; data-from=&quot;Limiter&quot; data-to=&quot;Storage&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1352&quot; y=&quot;808&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;HGET quota:daily&lt;/text&gt;&lt;line x1=&quot;1222&quot; y1=&quot;837&quot; x2=&quot;1481&quot; y2=&quot;837&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i15&quot; data-from=&quot;Storage&quot; data-to=&quot;Redis&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1355&quot; y=&quot;852&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;remaining tokens&lt;/text&gt;&lt;line x1=&quot;1484&quot; y1=&quot;881&quot; x2=&quot;1225&quot; y2=&quot;881&quot; class=&quot;messageLine1&quot; data-et=&quot;message&quot; data-id=&quot;i16&quot; data-from=&quot;Redis&quot; data-to=&quot;Storage&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;stroke-dasharray: 3, 3; fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1023&quot; y=&quot;941&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;✓ Available&lt;/text&gt;&lt;line x1=&quot;1220&quot; y1=&quot;970&quot; x2=&quot;825&quot; y2=&quot;970&quot; class=&quot;messageLine1&quot; data-et=&quot;message&quot; data-id=&quot;i18&quot; data-from=&quot;Storage&quot; data-to=&quot;Limiter&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;stroke-dasharray: 3, 3; fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;920&quot; y=&quot;1030&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;refund(tokens)&lt;/text&gt;&lt;line x1=&quot;822&quot; y1=&quot;1059&quot; x2=&quot;1017&quot; y2=&quot;1059&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i20&quot; data-from=&quot;Limiter&quot; data-to=&quot;Bucket&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;620&quot; y=&quot;1074&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;❌ RateLimitError&lt;/text&gt;&lt;line x1=&quot;820&quot; y1=&quot;1107&quot; x2=&quot;419&quot; y2=&quot;1107&quot; class=&quot;messageLine1&quot; data-et=&quot;message&quot; data-id=&quot;i21&quot; data-from=&quot;Limiter&quot; data-to=&quot;API&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;stroke-dasharray: 3, 3; fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;817&quot; y=&quot;1122&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;record_request(429)&lt;/text&gt;&lt;line x1=&quot;416&quot; y1=&quot;1153&quot; x2=&quot;1217&quot; y2=&quot;1153&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i22&quot; data-from=&quot;API&quot; data-to=&quot;Storage&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;247&quot; y=&quot;1168&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;429 Rate Limited&lt;/text&gt;&lt;line x1=&quot;414&quot; y1=&quot;1197&quot; x2=&quot;79&quot; y2=&quot;1197&quot; class=&quot;messageLine1&quot; data-et=&quot;message&quot; data-id=&quot;i23&quot; data-from=&quot;API&quot; data-to=&quot;Client&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;stroke-dasharray: 3, 3; fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1020&quot; y=&quot;1222&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;get_quota(weekly)&lt;/text&gt;&lt;line x1=&quot;822&quot; y1=&quot;1253&quot; x2=&quot;1217&quot; y2=&quot;1253&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i25&quot; data-from=&quot;Limiter&quot; data-to=&quot;Storage&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1352&quot; y=&quot;1268&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;HGET quota:weekly&lt;/text&gt;&lt;line x1=&quot;1222&quot; y1=&quot;1297&quot; x2=&quot;1481&quot; y2=&quot;1297&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i26&quot; data-from=&quot;Storage&quot; data-to=&quot;Redis&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1023&quot; y=&quot;1357&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;✓ Available&lt;/text&gt;&lt;line x1=&quot;1220&quot; y1=&quot;1386&quot; x2=&quot;825&quot; y2=&quot;1386&quot; class=&quot;messageLine1&quot; data-et=&quot;message&quot; data-id=&quot;i28&quot; data-from=&quot;Storage&quot; data-to=&quot;Limiter&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;stroke-dasharray: 3, 3; fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;920&quot; y=&quot;1446&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;refund(tokens)&lt;/text&gt;&lt;line x1=&quot;822&quot; y1=&quot;1475&quot; x2=&quot;1017&quot; y2=&quot;1475&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i30&quot; data-from=&quot;Limiter&quot; data-to=&quot;Bucket&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;620&quot; y=&quot;1490&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;❌ RateLimitError&lt;/text&gt;&lt;line x1=&quot;820&quot; y1=&quot;1523&quot; x2=&quot;419&quot; y2=&quot;1523&quot; class=&quot;messageLine1&quot; data-et=&quot;message&quot; data-id=&quot;i31&quot; data-from=&quot;Limiter&quot; data-to=&quot;API&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;stroke-dasharray: 3, 3; fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;817&quot; y=&quot;1538&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;record_request(429)&lt;/text&gt;&lt;line x1=&quot;416&quot; y1=&quot;1569&quot; x2=&quot;1217&quot; y2=&quot;1569&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i32&quot; data-from=&quot;API&quot; data-to=&quot;Storage&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;247&quot; y=&quot;1584&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;429 Rate Limited&lt;/text&gt;&lt;line x1=&quot;414&quot; y1=&quot;1613&quot; x2=&quot;79&quot; y2=&quot;1613&quot; class=&quot;messageLine1&quot; data-et=&quot;message&quot; data-id=&quot;i33&quot; data-from=&quot;API&quot; data-to=&quot;Client&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;stroke-dasharray: 3, 3; fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1020&quot; y=&quot;1638&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;decrement_quota(daily, cost)&lt;/text&gt;&lt;line x1=&quot;822&quot; y1=&quot;1669&quot; x2=&quot;1217&quot; y2=&quot;1669&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i35&quot; data-from=&quot;Limiter&quot; data-to=&quot;Storage&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1020&quot; y=&quot;1684&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;decrement_quota(weekly, cost)&lt;/text&gt;&lt;line x1=&quot;822&quot; y1=&quot;1715&quot; x2=&quot;1217&quot; y2=&quot;1715&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i36&quot; data-from=&quot;Limiter&quot; data-to=&quot;Storage&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1352&quot; y=&quot;1730&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;HINCRBY -cost&lt;/text&gt;&lt;line x1=&quot;1222&quot; y1=&quot;1759&quot; x2=&quot;1481&quot; y2=&quot;1759&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i37&quot; data-from=&quot;Storage&quot; data-to=&quot;Redis&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;416&quot; y=&quot;1774&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;Simulate generation (0.1-0.5s)&lt;/text&gt;&lt;path d=&quot;M 416,1803 C 476,1793 476,1833 416,1823&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i38&quot; data-from=&quot;API&quot; data-to=&quot;API&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/path&gt;&lt;text x=&quot;416&quot; y=&quot;1848&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;Calculate actual cost ≤ estimated&lt;/text&gt;&lt;path d=&quot;M 416,1877 C 476,1867 476,1907 416,1897&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i39&quot; data-from=&quot;API&quot; data-to=&quot;API&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/path&gt;&lt;text x=&quot;617&quot; y=&quot;1922&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;adjust_actual_cost(estimated, actual)&lt;/text&gt;&lt;line x1=&quot;416&quot; y1=&quot;1953&quot; x2=&quot;817&quot; y2=&quot;1953&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i40&quot; data-from=&quot;API&quot; data-to=&quot;Limiter&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;920&quot; y=&quot;1968&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;refund(difference)&lt;/text&gt;&lt;line x1=&quot;822&quot; y1=&quot;1997&quot; x2=&quot;1017&quot; y2=&quot;1997&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i41&quot; data-from=&quot;Limiter&quot; data-to=&quot;Bucket&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1020&quot; y=&quot;2012&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;increment_quota(daily, difference)&lt;/text&gt;&lt;line x1=&quot;822&quot; y1=&quot;2043&quot; x2=&quot;1217&quot; y2=&quot;2043&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i42&quot; data-from=&quot;Limiter&quot; data-to=&quot;Storage&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1020&quot; y=&quot;2058&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;increment_quota(weekly, difference)&lt;/text&gt;&lt;line x1=&quot;822&quot; y1=&quot;2089&quot; x2=&quot;1217&quot; y2=&quot;2089&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i43&quot; data-from=&quot;Limiter&quot; data-to=&quot;Storage&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;817&quot; y=&quot;2104&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;record_request(200, actual_cost)&lt;/text&gt;&lt;line x1=&quot;416&quot; y1=&quot;2135&quot; x2=&quot;1217&quot; y2=&quot;2135&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i44&quot; data-from=&quot;API&quot; data-to=&quot;Storage&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1352&quot; y=&quot;2150&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;LPUSH requests:key&lt;/text&gt;&lt;line x1=&quot;1222&quot; y1=&quot;2179&quot; x2=&quot;1481&quot; y2=&quot;2179&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i45&quot; data-from=&quot;Storage&quot; data-to=&quot;Redis&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;617&quot; y=&quot;2194&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;get_remaining()&lt;/text&gt;&lt;line x1=&quot;416&quot; y1=&quot;2225&quot; x2=&quot;817&quot; y2=&quot;2225&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i46&quot; data-from=&quot;API&quot; data-to=&quot;Limiter&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;920&quot; y=&quot;2240&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;get_remaining()&lt;/text&gt;&lt;line x1=&quot;822&quot; y1=&quot;2271&quot; x2=&quot;1017&quot; y2=&quot;2271&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i47&quot; data-from=&quot;Limiter&quot; data-to=&quot;Bucket&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;1020&quot; y=&quot;2286&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;get_quota(daily/weekly)&lt;/text&gt;&lt;line x1=&quot;822&quot; y1=&quot;2317&quot; x2=&quot;1217&quot; y2=&quot;2317&quot; class=&quot;messageLine0&quot; data-et=&quot;message&quot; data-id=&quot;i48&quot; data-from=&quot;Limiter&quot; data-to=&quot;Storage&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;fill: none;&quot;&gt;&lt;/line&gt;&lt;text x=&quot;247&quot; y=&quot;2332&quot; text-anchor=&quot;middle&quot; dominant-baseline=&quot;middle&quot; alignment-baseline=&quot;middle&quot; class=&quot;messageText&quot; dy=&quot;1em&quot; style=&quot;font-family: arial, sans-serif; font-size: 16px; font-weight: 400;&quot;&gt;200 OK {cost, remaining, reset_times}&lt;/text&gt;&lt;line x1=&quot;414&quot; y1=&quot;2363&quot; x2=&quot;79&quot; y2=&quot;2363&quot; class=&quot;messageLine1&quot; data-et=&quot;message&quot; data-id=&quot;i49&quot; data-from=&quot;API&quot; data-to=&quot;Client&quot; stroke-width=&quot;2&quot; stroke=&quot;none&quot; marker-end=&quot;url(#mermaid-0-arrowhead)&quot; style=&quot;stroke-dasharray: 3, 3; fill: none;&quot;&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/p&gt;
&lt;h3 id=&quot;key-design-patterns&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#key-design-patterns&quot; aria-label=&quot;key design patterns permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Key Design Patterns&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Cost Estimation Strategy&lt;/strong&gt;: Before calling the LLM, the system estimates cost based on input tokens and expected output length. In this demo, costs are randomly simulated (1-5 budget units) to mimic real-world variability. In production implementations, cost estimation uses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Input token count from prompt tokenization&lt;/li&gt;
&lt;li&gt;Maximum output tokens from the &lt;code class=&quot;language-text&quot;&gt;max_tokens&lt;/code&gt; parameter&lt;/li&gt;
&lt;li&gt;Per-model pricing catalog (e.g., GPT-4o: $2.50/1M input tokens, $10.00/1M output tokens)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example: a prompt with 800 input tokens and &lt;code class=&quot;language-text&quot;&gt;max_tokens=300&lt;/code&gt; would estimate (800 × $2.50/1M) + (300 × $10.00/1M) = $0.005 = 5 budget units. See &lt;a href=&quot;https://handsonarchitects.com/blog/2025/denial-of-wallet-cost-aware-rate-limiting-part-2/#token-bucket-algorithm&quot;&gt;Part 2&apos;s Token Bucket section&lt;/a&gt; for detailed cost calculation formulas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Optimistic Locking&lt;/strong&gt;: The system pre-consumes the estimated cost before processing the request, then refunds the difference after determining actual generation cost. This prevents race conditions while ensuring accurate accounting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rollback on Failure&lt;/strong&gt;: If any quota check fails (daily or weekly), previously consumed tokens are refunded to the minute bucket to maintain consistency. This ensures that a failed request due to quota exhaustion doesn&apos;t also consume the minute-level tokens.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Continuous Refill&lt;/strong&gt;: The token bucket adds tokens proportionally based on elapsed time rather than discrete intervals. The bucket stores a &lt;code class=&quot;language-text&quot;&gt;last_refill_timestamp&lt;/code&gt; in Redis; on each request, the system calculates elapsed time and adds tokens accordingly (e.g., if 60 seconds elapsed at 6 tokens/minute, exactly 6 tokens are added; if 30 seconds elapsed, 3 tokens are added).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Background Metrics&lt;/strong&gt;: An async task records snapshots every 10 seconds for historical trending without blocking requests. This enables the real-time dashboard to show rate limit state over time.&lt;/p&gt;
&lt;h2 id=&quot;rate-limiting-architecture&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rate-limiting-architecture&quot; aria-label=&quot;rate limiting architecture permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rate Limiting Architecture&lt;/h2&gt;
&lt;p&gt;The implementation uses three complementary layers of protection, each serving a distinct purpose in preventing Denial of Wallet attacks:&lt;/p&gt;
&lt;h3 id=&quot;1-minute-bucket-token-bucket&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-minute-bucket-token-bucket&quot; aria-label=&quot;1 minute bucket token bucket permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Minute Bucket (Token Bucket)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Capacity&lt;/strong&gt;: 24 tokens (maximum burst size)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refill Rate&lt;/strong&gt;: 6 tokens/minute (sustained rate limit)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: Prevents rapid successive requests that could quickly exhaust quotas&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This layer allows legitimate bursts (like an agentic workflow spawning multiple LLM calls in quick succession) while maintaining a long-term average rate. The bucket can accumulate up to 24 tokens during idle periods, enabling users to burst above the steady-state 6 tokens/minute rate briefly.&lt;/p&gt;
&lt;h3 id=&quot;2-daily-quota-short-term&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-daily-quota-short-term&quot; aria-label=&quot;2 daily quota short term permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Daily Quota (Short-term)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Total&lt;/strong&gt;: 40 tokens per window&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reset Window&lt;/strong&gt;: 5 minutes (demo) / e.g. 24 hours (production)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: Limits sustained usage within short periods&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This quota prevents scenarios where a user stays just below the minute-level rate but maintains that usage continuously, consuming far more than intended over hours.&lt;/p&gt;
&lt;h3 id=&quot;3-weekly-quota-long-term&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-weekly-quota-long-term&quot; aria-label=&quot;3 weekly quota long term permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Weekly Quota (Long-term)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Total&lt;/strong&gt;: 100 tokens per window&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reset Window&lt;/strong&gt;: 20 minutes (demo) / 7 days (production)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: Prevents extended abuse over long periods&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The weekly quota is the ultimate cost ceiling. Even if a user carefully paces requests to avoid minute and daily limits, they cannot exceed 100 tokens per week. This provides predictable budget protection.&lt;/p&gt;
&lt;h3 id=&quot;hierarchical-enforcement&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hierarchical-enforcement&quot; aria-label=&quot;hierarchical enforcement permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Hierarchical Enforcement&lt;/h3&gt;
&lt;p&gt;The three layers are checked in sequence: minute bucket → daily quota → weekly quota. A request must pass all three checks to proceed. This creates a cascading protection model where exhausting the weekly quota blocks requests even when daily quota resets. We&apos;ll see this behavior demonstrated in Scenario 3.&lt;/p&gt;
&lt;h2 id=&quot;scenarios&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#scenarios&quot; aria-label=&quot;scenarios permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Scenarios&lt;/h2&gt;
&lt;p&gt;We&apos;ve developed three load testing scenarios using k6 to demonstrate how the rate limiting system behaves under different usage patterns. Each scenario automatically resets the system state at the start to ensure consistent results.&lt;/p&gt;
&lt;p&gt;You are welcome to clone the repository and run these scenarios yourself. All necessary instructions are provided in the README of the &lt;a href=&quot;https://github.com/handsonarchitects/denial-of-wallet-demo&quot;&gt;denial-of-wallet-demo&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;scenario-1-short-bursts&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#scenario-1-short-bursts&quot; aria-label=&quot;scenario 1 short bursts permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Scenario 1: Short Bursts&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Duration&lt;/strong&gt;: 6 minutes&lt;/p&gt;
&lt;p&gt;This scenario shows how the system handles burst traffic that stays comfortably within all rate limits.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Traffic Pattern&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First burst (1 min): 10 requests at 1 request per 6 seconds&lt;/li&gt;
&lt;li&gt;Pause (4 min): No requests - token bucket refills naturally&lt;/li&gt;
&lt;li&gt;Second burst (1 min): 10 requests at 1 request per 6 seconds&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Expected Behavior&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;100% success rate (all 200 responses)&lt;/li&gt;
&lt;li&gt;Token bucket fluctuates between bursts but never exhausts&lt;/li&gt;
&lt;li&gt;Daily and weekly quotas decrease gradually (~40 tokens consumed total)&lt;/li&gt;
&lt;li&gt;Dashboard shows smooth green metrics throughout&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Demonstration&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;The dashboard after completing Scenario 1 shows healthy quota levels across all three tiers. The minute bucket recovered fully during the 4-minute pause between bursts, demonstrating the continuous refill behavior. Daily quota dropped from 40 to 19 tokens (21 consumed), and weekly quota from 100 to 54 tokens (46 consumed).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/663f2bc7434db22248d4a6577f0386ac/ad00e/scenario-1-finished-dashboard.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 156.92307692307693%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAfCAYAAADnTu3OAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEEUlEQVR42q1Vy24cRRTtD0AsECsUSBArxIewYZWgsOIrQAKJkLDg8QVICExCYAMSIlgRsSFSFjwSW4aImBBZSIA9M+5p9zz63VXd9TjcW90z02OHkAA9Oupbt26dun1f41V1jUoppIXCOKpRSlqTriad29MacZIjSSWt1dJeWSlMRlPkBesavSdrjUoWKH7/CkHgE2EFWRERQSqgiv/A+PZnGE2mpFdLe2XvGobbXyLOykZfM6EykMUUyeAmhqOIbivnhwQTju8gHO4hnMQQs8vooJQCRfAzBv4QUZItCJXS5KqGoNuFlG6D10o3qDVdWDd7dWvr9PSu+FznTM2EQlokmUFWGBTCovsQF2BJZ7V7GYPlx9pDSwtP06kypyALA60cgwP/UkEeiBJaTCFKiSLjz5Lz/eZnHJzOGv5khWRSIwqBeEzejo1DPNak53ezTicNEtJnk9laEyyyyKLMLDlEHnLwJ/4I8TBBEpaIQzVH1L6nhDDQOBgqQo3AVxgSAl8j6E0R7voY9SLIvIZnjMUomGLwpw+/FyAcHJBR4ORhP8SgR+hP4Q9G2N87QJ8w2AvpHaK3G9CbbPtj9Ol8nhbwZmHVFFAuYkVxqA1lzCgnMzStjTEuYor0pv0tZIoinWcbr6QsR6lGWRKRoExqHHlcou2RpB62gmVC3dbCL7GPT/s/4dL+Laz621jZvYGLv32HS7e/RVRMqC0OsD0MceHXTXzev4kv9rcJtxz4zFjmrkC8spKO8O2dr/HQ6kt4cv0cHl97HU8QHrvyGp6++iZ61En8fLS7gUcuv4Lj62fnNsfWzrj11nTP2Xh5WTrhnZ1v8PDqy3hq/Q0cXzuLE2vncOzKGTxz9S30W8KLRPjo5VfdpSdaMCmvf5wR1roJ2id7W3j2+3fxwuYFnLz+Pk7dWMFJwotbHyMUqbNZG97Bcz+8h9Ob58nmA5y6voLnNz7E6Y3z2EkPGkJuF3so2i6jHV1No0oIAW6CeQrYptOLdpZlWTW9XErTpP4uWDrUwuKojevlQkgaWdLNOp4iJXnCcWWZh2hBMk8RnkpJmrn9ZtrwbKyQ5bmT3RQieNOCjMrCFSeXkNA8B2vnvjLNqGK9G2eqke8Fb5zGyOhmN2OoDUsiY1CDgCcRk3B7duUuWL9YE2FU5jSaBMUBTslZ5xa0rfHsQFf+exBhmESIo3jJw+K/eMgxFBT4WdorrRysaYaC1s1gYHTlhW55vYihnXmolmLY/eT78zBPIYrGQ/ZKtlluYrjwULssq7t4uOy1NyHCIi/aPyFLhFRfSv2rGLrCHv2PZeMIuWwkdcvMw6pbNubBysYRctmkSXrPsuHLWOYW/EcPJ0XqCvtoDO0DZ9kRptTHXUL3yS2huY9PPkz4Fwm1PFh/GGwzAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Dashboard showing healthy quotas after Scenario 1 completion: minute bucket at 18 tokens, daily quota at 19/40 tokens remaining, weekly quota at 54/100 tokens remaining, with all 20 requests successful&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/663f2bc7434db22248d4a6577f0386ac/3c492/scenario-1-finished-dashboard.png&quot;
        srcset=&quot;/static/663f2bc7434db22248d4a6577f0386ac/ff46a/scenario-1-finished-dashboard.png 325w,
/static/663f2bc7434db22248d4a6577f0386ac/a6d36/scenario-1-finished-dashboard.png 650w,
/static/663f2bc7434db22248d4a6577f0386ac/3c492/scenario-1-finished-dashboard.png 1300w,
/static/663f2bc7434db22248d4a6577f0386ac/ad00e/scenario-1-finished-dashboard.png 1366w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Key observations from the k6 log output:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Transitioning to stage:&lt;/span&gt; FIRST_BURST
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0056&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Success &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Daily&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Weekly&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;75&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Cost&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0062&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Transitioning to stage:&lt;/span&gt; WAIT
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0302&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Transitioning to stage:&lt;/span&gt; SECOND_BURST
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0303&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Success &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Daily&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;39&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Weekly&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;74&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Cost&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0360&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Success &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Daily&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Weekly&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;54&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Cost&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that at the second burst (second 303 -&gt; ~5th minute), the daily quota has reset from 15 back to 39 tokens - the 5-minute daily window reset occurred during the 4-minute wait period. This demonstrates automatic quota recovery.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Learning&lt;/strong&gt;: Token bucket handles burst traffic gracefully and refills during idle periods. The continuous refill algorithm ensures smooth rate limiting without artificial request delays.&lt;/p&gt;
&lt;h3 id=&quot;scenario-2-daily-quota-exhaustion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#scenario-2-daily-quota-exhaustion&quot; aria-label=&quot;scenario 2 daily quota exhaustion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Scenario 2: Daily Quota Exhaustion&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Duration&lt;/strong&gt;: 8 minutes&lt;/p&gt;
&lt;p&gt;This scenario shows what happens when sustained traffic depletes the daily quota while the weekly quota remains healthy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Traffic Pattern&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Exhaust phase (5 min): Steady requests at 5 requests/minute (12s intervals)&lt;/li&gt;
&lt;li&gt;Wait phase (1 min): Continue requests to monitor exhaustion state&lt;/li&gt;
&lt;li&gt;Recovery phase (2 min): Verify quota reset and recovery&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Expected Behavior&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Initial requests succeed (~20 requests consuming ~40 tokens)&lt;/li&gt;
&lt;li&gt;Daily quota exhausts, 429 responses begin with &lt;code class=&quot;language-text&quot;&gt;daily_quota_exceeded&lt;/code&gt; reason&lt;/li&gt;
&lt;li&gt;At 5-minute mark: Daily quota automatically resets to 40 tokens&lt;/li&gt;
&lt;li&gt;Requests succeed again after reset&lt;/li&gt;
&lt;li&gt;Weekly quota remains healthy throughout (consumed less than 100 tokens throughout the scenario)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Demonstration&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;After 5 minutes of sustained traffic, the daily quota is fully exhausted while the weekly quota still has capacity:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/47b12fd8e5c232e016e201acb0585a00/4352a/scenario-2-after-5-minutes-dashboard.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 158.15384615384616%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAgCAYAAAASYli2AAAACXBIWXMAAAsTAAALEwEAmpwYAAAEOElEQVR42p1Wy24cRRSdH2DBgg0RQoI9G3YsIpYoQqxYs+AXQAiJmLBigZCyAT4ACRRhNiGJYzkRgghWiZCJCUoiyNgzfs/0TD+r69HVh3Ore9pja2LhtHRcU69Tp+65deWedRUESQnEuYexTd+144ZI8wq5avpHcw6l9ZgWQFEejfe0sdDGIP13FdHWPSjjQ99Yh2bOYjxJMU3yrm+kdYAaP8ZoYxlpMoWmEBnvyYSJ+5g+voGdvT2oUjebWkKlDfYPDzGOJih1S8jNWpdQm2vYfPQHJtO429MT6YaKcqUDThIKioKblT5SKHM8SA5LC4u8UJ3ynjE1OzVPZ/x43bpGh/Cx9dL3zcD8vOefqqrgORf6bHuq9EhTjyzzVOLDoLBIkygeohW8iaAVr587uMrgaV9N1p5Ij8eeoMujGnFUIyHiER0c0eGJQza1bCu2dDutUFCAFmd5qwDezDKuVeWpsCiQRTmyEduxQjrWSEcGycghjRCQBNTH+tODGtNDBAQhhOaNqFBj8M8Q/Ydb6D8a8PcOsYut0G5j+GQPw/4BBv0Ig60Ew022/TGGgwTbw5xtzrbAYEAxCU2RmBnj6KJBEVymq/ydZlSc5cHhIldIkhSq0AEFkacJY8qxPEORZexncCEPg8vMRdugc/cZvmCKpIs4HVNVJCc75hed3Ncp4ixGzlfgLJ3WCUpmgbjtK8fN/gSYGZ4xtNYG9kt/38D5O5dx4fdv8NZvX+ONXy/j/NoXePfnr5CXKQlJTkJ504ZuWt5kHn5GmNFlSd73732H5699hFdWP8PLNz/Fq2zPrS7h9V++hK6aQ2vPCmJjxiaR93oMtbdNYmujA+F7977Fcz99gJduLuHFlU9wbuUiXrj+MV67/TlyqwPhcv8O3lz5EO/cvoS3by11uLB2EfejJ2FNT2TKd3eyias7f2Jl7wGuDtdxfXcD1/Y2cOvgISsQCZm4D5J9fL+9jh93/8KVwTqucN0y1/2wex+HOgvCQtoI52nuyguQWjc7/FSXU77PccxXoVh1GCsltZAuamdZQA2sr0I1EsKKhDJeaBOMMVUV1goqX7WmJEwHvhZfSyFg0jKZK1+HIitw3JTlLK6cE0Ipd0U2gS1jHtLMzyCVpxfFU5RlGe7flHUTrh8UUcUMs/IUwJt4RxFt6ToCFY7imM+uDBs0F+TOhphaiVsLG65bd3DScv38WINAOA2EotBQYU6F8GhNqDuF80pO9o8pnGQpa1sZbJbgKsZFqvMsJgLHg2TxDE2V9gvRi5IY5VMV+rmUOZPC5rVIGijGkIZ3m85GWLcxZM2TT1NhJgpPuHwmwplCcdlSYblA4f83JSicUKFqFVoq1MEgMeLspgSFSauwpkLXKqw7sjMTNjFUC2P4TKZEncLFLi8iOJVQ8dFb/q8SypQ8flYNtOVcwlCf+L2oP4//AJQRnUgprtTEAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Dashboard showing daily quota exhausted at 0/40 tokens while weekly quota remains healthy at 60/100 tokens at the 5-minute mark in Scenario 2, demonstrating daily quota enforcement&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/47b12fd8e5c232e016e201acb0585a00/3c492/scenario-2-after-5-minutes-dashboard.png&quot;
        srcset=&quot;/static/47b12fd8e5c232e016e201acb0585a00/ff46a/scenario-2-after-5-minutes-dashboard.png 325w,
/static/47b12fd8e5c232e016e201acb0585a00/a6d36/scenario-2-after-5-minutes-dashboard.png 650w,
/static/47b12fd8e5c232e016e201acb0585a00/3c492/scenario-2-after-5-minutes-dashboard.png 1300w,
/static/47b12fd8e5c232e016e201acb0585a00/4352a/scenario-2-after-5-minutes-dashboard.png 1364w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Near the end of the scenario, after the daily quota reset, requests succeed again:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/ddfd598a531c178f10215f3634c8363e/bb27a/scenario-2-finished-dashboard.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 156.3076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAfCAYAAADnTu3OAAAACXBIWXMAAAsTAAALEwEAmpwYAAAETUlEQVR42q1WTYscRRieu+QsGtSIV8lR1IP/wJOHYEDw4tFTEhC/oqDmL4SAGxGSm4H4sbsJAT8OGxONaLK6K5LZ2Znp6Zntnpn+ququ7qp+fN/q7pkdZ2WDseCZqq5666nn/ahiWirPkRcFIlHAD3LILEczx70qNIIoQxjX8zyXV2si0/CnORI539PKaDHLJJL7axi6DmSaI1NkQMgKQE3vw9/6Ct4kns8rZddE91s47bsIE2XX8twSGmTJGFH/NlxvikTI2caUCb178IcdUhIhzZRVYkWkEnJwB4PBgDyI54QFyWSpaa5JXWY38UJBrto1rZEq9kJZO0ZBczYkRbWHFTfzrTQrKT4GsTQQaYn9TRv6KWmu1LYzBoutLPHP1tK0K4kKiJBcTw1UZlBQGHRRIpCkjuKrswlSqSAoMUWhmGmGEoZ4G6ByOZzkCMdA4DFIsQ/7HY2NRTylQwMDGev60BI59Tn3qqTDSX1Z2iNaHGTPGcHbHcIfhPAGEXw3wXgosefm2BsaeCMC9T5h7BoMHYNBv4Tb9jDaceH3ppg6ERRVSKsk5tFwgu7OCM7uAL12H07HRb+7R/DhdEfo7dJaz0evO0GfDnScEP3+hNaG1s7tuuj81YNIUiKsg2koBsYmguJH4gv61kbTPMFoGyNtCvommAolrWn6tpE0tcuS4jCNKP2C40LEeilxVaLLA5O6nGWuHW53gwEudX/GF/1fccX5DRc6G7i4/T2ubP6AUEwASa6NPGy1f8Hu3j30/C3CH4Tf0aexymVFKLLUDj7evoZHrp7C0+tn8cTqe3hy7X08tvoOnr3xCQZyam0utb/D8S/fxItrZ/D86mm8QHjum1N4af0t/Bn0K8JYCjs4t30dR66exjPrH+ApIju2dhZHV9/F8RvniDC0Nud3NnDk67dx9NqHeHy9wqNkd+z6R9gM3Yowr12+TO6+vHEer/30OU7c/BQnb13EiVsreOPOZfhZYm1W3U28cvMC2XyGV39kmxWcvL2C12lPR4wrQi6bsmwqv06CaeaqpuiupmkKrfU+G7NgU/EQYUaVHsR8j42t9oOwf1M1Z+zxCza1XUvQaxGLlF4LbV+NKIntq8FjISWNDUJ6npox36wojmcvTwNVFLYWWxMRQQhhD1B0KQVdfl5gUgYXetMfBlba8sIpgqDKIp8U55kNp9H84mgbT35AbFwPAxMGMoGUqVXILqV2M11BJiSw2qY/DJbQj4KZQo5DpCqFTMKvtnW/7h+IcForZIlck7LIrXwuEYbh+NT9HA2JWYAlHMchIspipVAjyecKK6KDXNY1zLLCOAxslrkVpE5lqV1gssKWgpn1FYhIxTBZAEP2SwonSYQkqa5WXpfNQ7nsUVKiKJolJV5w2fxLUsySuzOXA9EkBbaAU5uU+uX+L2XDhR2GlcLs/yibiaB7Sn8/mGReNg+hMOH/KDOXm5tSLhA9OCHwN9WcP9FLmaukAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Dashboard showing recovery after daily quota reset with daily quota restored to 28/40 tokens and weekly quota at 37/100 tokens, demonstrating automatic quota reset behavior&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/ddfd598a531c178f10215f3634c8363e/3c492/scenario-2-finished-dashboard.png&quot;
        srcset=&quot;/static/ddfd598a531c178f10215f3634c8363e/ff46a/scenario-2-finished-dashboard.png 325w,
/static/ddfd598a531c178f10215f3634c8363e/a6d36/scenario-2-finished-dashboard.png 650w,
/static/ddfd598a531c178f10215f3634c8363e/3c492/scenario-2-finished-dashboard.png 1300w,
/static/ddfd598a531c178f10215f3634c8363e/bb27a/scenario-2-finished-dashboard.png 1371w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Key observations from the k6 log output:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0283&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Success &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Daily quota remaining&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Weekly&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Cost&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0295&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Daily quota &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; not enough tokens to process the request &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;left &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0307&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Transitioning to stage:&lt;/span&gt; WAIT
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0367&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Transitioning to stage:&lt;/span&gt; RECOVERY
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0367&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Success &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Daily quota remaining&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;38&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Weekly&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;58&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Cost&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;At timestamp 0283 (~4:43 minute), the daily quota reaches zero while weekly quota has 60 tokens remaining. The next request (0295 -&gt; ~4:55 minute) is rejected. After the 5-minute reset window passes, the recovery phase (starting at 0367 -&gt; ~6:07 minute) shows the daily quota has reset to 40 tokens (38 after first request), and requests succeed again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Learning&lt;/strong&gt;: Daily quota limits provide short-term protection and automatically reset at configured intervals. The system gracefully rejects requests when quotas are exhausted and provides &lt;code class=&quot;language-text&quot;&gt;Retry-After&lt;/code&gt; headers to guide clients.&lt;/p&gt;
&lt;h3 id=&quot;scenario-3-weekly-quota-exhaustion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#scenario-3-weekly-quota-exhaustion&quot; aria-label=&quot;scenario 3 weekly quota exhaustion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Scenario 3: Weekly Quota Exhaustion&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Duration&lt;/strong&gt;: 25 minutes&lt;/p&gt;
&lt;p&gt;This is the most complex scenario. It shows how weekly quota enforcement persists even when daily quota resets multiple times, proving the hierarchical nature of the protection layers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Traffic Pattern&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Exhaust phase (19 min): Steady requests at 3 requests/minute (20s intervals)&lt;/li&gt;
&lt;li&gt;Wait phase (2 min): Continue requests to monitor weekly exhaustion&lt;/li&gt;
&lt;li&gt;Recovery phase (4 min): Verify quota reset and recovery&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Expected Behavior&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0-15 minutes: Requests succeed, daily quota resets every 5 minutes (at 5, 10, 15 min marks)&lt;/li&gt;
&lt;li&gt;~15-17 minutes: Weekly quota exhausts (~50 requests consuming ~100 tokens)&lt;/li&gt;
&lt;li&gt;~17-21 minutes: Requests fail with &lt;code class=&quot;language-text&quot;&gt;weekly_quota_exceeded&lt;/code&gt; even when daily quota resets&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cascading Limit Demonstration&lt;/strong&gt;: Around the 20-minute mark, daily quota resets but requests STILL fail due to weekly exhaustion - this proves hierarchical enforcement&lt;/li&gt;
&lt;li&gt;~21-minute mark: Weekly quota automatically resets to 100 tokens (20 minutes after test start)&lt;/li&gt;
&lt;li&gt;21-25 minutes: Requests succeed again&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Demonstration&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;After 5 minutes, both quotas are healthy with daily quota showing first consumption:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/d06a9b07a14d9652b1e5542a699c5fb8/b1001/scenario-3-after-5-minutes-dashboard.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 140%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAcCAYAAABh2p9gAAAACXBIWXMAAAsTAAALEwEAmpwYAAADz0lEQVR42sVVOY9cRRB+PwEyJMSRIEIyroTQQkQEZPAzQEICS4gEiUMEIALkwEQOwVjYQrAgrw/hRZZYCLyrWc+16515+455d98fVf3eG814ZwOT0KNP1dWvu7qqvqqeQGkDqTSEsEhyDUWS9X5dkswqh7zUrb7yTUk6k2k0wizXA6kUFCl3whEu7e1gNz6CUQZCKghFxssQycEW4kXZrnXgPX9Fh7h07w7+Pjlsz5CtwJDVqC5x83iA4WSKuycTVELQJXRQAzK+h3C4g+OTlDyRS4PsxPZsgL3xEPM0obXW+0AbA605VNrckCGxHhZHUDUKZVmfDplQVw2dk6shc34sytrRQQfnsBxuw2x1uJWJ6xBY61AWGuXCoioseeigJYEuSjK6qGggixhlLpCnCk0tYI1rYV1ntoUji4GxxGIqkcVAFgFp6BDPLNI5yXk3D4nNsJWMLHIEizwmJK4FzRU5EijKXzKPEE+JzaMI6XGCxCNFMivIYEWG6w5NB4E4NASN6FgSFMIH7L1FwCmYH8cYDmaYDOeYDmcY35+RnGN8cITRYILxYIrJ/cNOJ0l7JuMU03Hkz0xGIa2FRFyDgON21nIGYGChKQXSaGhnYQiKqsDSdwaPXrY5s+s6/QJBcS+IjIq6QTV0wOA/D6aGSCFBRqZViq1wH9vRADfjA1ye/YMro7vY3v8TCbFsqxmyhUaaEOtaUiR2DdZ1LHO78LgwuoXHLr+Lp69+iCd/+sDjiSvv47lrH2E/n/vrzTK804ND9wbLuiaNDd7G4z++h2evncdTV8/jGQIbff7nj7HHBmlshXt4648LeGfnO7y9c5HkxaUcFpHfE+guad8f7eK161/ijVvf4Nz1r/D6ja9xjvDm7W8xLGO/54cHu3jhl0/w6u9f4MVfP8VLW5/h5d8+xyuE/tLAddm0xJi02qNR1NeUp14XUqJpGupV5fcxNFcCV0Cnuz6HUjsUFbHc2I740791Js/e41luhEJO/SqkAXdNQ09XUZb+dtYryjG/KsZQy+W51/tv/V5ltNeZmCBvaiyyhb+FFwR9rChkbnxmlQ+zZPChfr4JXDpBWpWIkqR9fsiIIAMlGaS0eKPa3+y6uVnON8HnMBc15bD0CnvIrSYo+a5rN7PSeqvzTXC9hzF5iKWHGoUSvjYf9ur/8pDevDRB/+qwh6X3sL1Ad+w9PD/TYNZUVDbFkhRJXtTewzZkLpdHCnlRV0iz7EyDj5zDxIeccoR+sfGktGXDTxv/NRpfk+vzTfB1yK8zd0L/N8iLxrql3vZo921tfTP+Bd/7XpaOjtnQAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Dashboard at 5-minute mark in Scenario 3 showing healthy quotas: daily quota at 26/40 tokens, weekly quota at 86/100 tokens, demonstrating normal consumption before weekly exhaustion&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/d06a9b07a14d9652b1e5542a699c5fb8/3c492/scenario-3-after-5-minutes-dashboard.png&quot;
        srcset=&quot;/static/d06a9b07a14d9652b1e5542a699c5fb8/ff46a/scenario-3-after-5-minutes-dashboard.png 325w,
/static/d06a9b07a14d9652b1e5542a699c5fb8/a6d36/scenario-3-after-5-minutes-dashboard.png 650w,
/static/d06a9b07a14d9652b1e5542a699c5fb8/3c492/scenario-3-after-5-minutes-dashboard.png 1300w,
/static/d06a9b07a14d9652b1e5542a699c5fb8/b1001/scenario-3-after-5-minutes-dashboard.png 1380w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;At the critical moment when weekly quota is exhausted but daily quota still has capacity:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/c49c4e247f4087ea614bbb073cb0fdfb/bb27a/scenario-3-weekly-exhausted-dashboard.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 156.3076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAfCAYAAADnTu3OAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEaUlEQVR42q1WPWwcRRQ+IehoaOhoaUNHDQWImooaKgpAFBEVHeJHSIgE0VAgEIoUCZMICQiCkMRAhKWQhF879t35zvbau/ezf7O7szM7ux/vzdzeXWwRbImznmb2zb5v3u+37qiqgq408qLCTphDSvesSa+0hjI1klQgiVN6NlantTuTpcYk0hC5dnqy6Sg6LJXG1YNNnPv7OtbG2zBkyLpSN1AiQPjbJxjvbUDqmvTKGvN5Vkh4/ghxktlnxuqw8bTIcDMYYEyHN8dDiFJaL8hZqHALE28Do2lKHpVQbKgWgH4wshHMAStjUJGrlSJPyUCryrre6jWtki6VpbJ6e1aZ+Z71fEFr0ylVgySrIYoGmWzQNJj/6hruueGNW/7r16nJQsRUlLSGLGooWaM2FgWpJM9kAVOGdCYhEgqV0lHTTXXdzFYnTcPSUA7JzSRSiEZAGDSIR+TxmGTaoBBUlLKhdJDnbMR/zb0ELoeTfR/B0MfIixDsF/C9HMG2B38Yw9/h54pEY0S68d4E0xG1UVIiTVJkoUcXR8jz3Bayw6j+QYjtno9BdxeD/j6G2z6GtO70d7Ez8K0Mex4GvX0r/Tu76G269/tbAa0e2e8hTQsCtGFQHmY5cFLD1AbG6jlXXHFl32nztcjbIp82ZK5ylBpEokKYUuvou0vZLJf90DNvD+exw+jsjX1h9qIvU1wZb2GNpienxg3SMQ6owf0ow8gv0N2N0NsTduRcey0BGmrSWGb4M9nHjXBI4DXevvMdHvryNB7/4V1spgFe+PUcTn3/Ft7Y+BZr/g4eW3kPj55/B18P1y1gVVeLtkHV4Oegi0cuvY4HLr6Clb3bONO9hvsvvIxTl9/EeuLjidUzuO/CS3jx1nn8OOni4Uuv4cGvXsWKd8sCakoTYc4A6bceH+C5Xz7G06sf4KdxD194t/Hk1ffx/I3PMMynOP37RTx17SzO0kV/xB6evf4Rnln9EGtxHxt/5fj08wG+uXxATW+4yi57NgezXFhNvah6xVRV0MTQvDazwvC71jtyLdclpFHOQ6aoRBhiDtciLUg9EwewNCVchPbsUAfYWS5lCZEVljyZ6wRREU8PU1UhJfVfTXyXur1xrMN05ch2wTospmYP05SGPrE3GxqdikiAD5i6jKUwJ1x9Q+zNFzCRZAmBKadrhdunI0SK6TS07nKOFLGL3RNYyyS8n6eCjGROzJSx/ihBdDIa6izLLffpylE/9zl7sixszFJRm2nlhPetnsV6OBEC/jik/ACC2HpKOaXIISmcVgpqBy5eK2omyzpJUhFoJ6EQRZbZwdSGvyPKtgSHuSzLZPpvYkOO8gxhFNm8KTIU1FNcIVsI0xbCzBjl3mIBY/rixVRpBmEPc33UQ674sT1MipwAkzlgcSjktsonCFlg+n+GnJZUFGodC8KTQP+G8OdyuVnb9ViAYUYehq6xS8pVqpyHyxNwwqLkRA5ilkNDObzbQycnAMwVk0MbMjUyU9QRwOOH/A/KqE+nSRHSMAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Dashboard showing cascading limit enforcement: weekly quota exhausted at 0/100 tokens blocking requests despite daily quota having 24/40 tokens available, demonstrating hierarchical enforcement&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/c49c4e247f4087ea614bbb073cb0fdfb/3c492/scenario-3-weekly-exhausted-dashboard.png&quot;
        srcset=&quot;/static/c49c4e247f4087ea614bbb073cb0fdfb/ff46a/scenario-3-weekly-exhausted-dashboard.png 325w,
/static/c49c4e247f4087ea614bbb073cb0fdfb/a6d36/scenario-3-weekly-exhausted-dashboard.png 650w,
/static/c49c4e247f4087ea614bbb073cb0fdfb/3c492/scenario-3-weekly-exhausted-dashboard.png 1300w,
/static/c49c4e247f4087ea614bbb073cb0fdfb/bb27a/scenario-3-weekly-exhausted-dashboard.png 1371w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;After both quotas reset and the system fully recovers:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/ab420098fb482176682fb29c2c6fdbe1/d8104/scenario-3-finished-dashboard.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 156.92307692307693%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAfCAYAAADnTu3OAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEc0lEQVR42qVWzYscVRDvu14E/wmv4lW8iaCoKOwpEk8exJOCHvwDVLx4MEQSUSMoJhLRRBI9KJrsJyYh63652Z2dmd2dnume6e/v1/36Z9V7PbOTOFkMaajp6tev6v2qflXVY+SFgBAlvCjHHdtHlpfgtULoe16W8IMMXlAgF6J5R0J33rtNNl6c6zWyMfgnznNc2V/Dha1lrAzbkEKSITkogcLbxfCfX2A5oT6gEd6zaLXw7fo8bltdlKJSBxqsuGmCdfsAztDBhtNDRs4UAnKYO9sYWfsYeiEhKhokAinpm64J2x4hCONJVEZZVmApilJt4vAFr1VaREloaY2djffqda0nWU7vxMTGyPMaQSQRpZJe1qgB1HWNSta0iZ6lJCkh6bmqavVufB1pR8+GJIPQE0hCiSyRdKqcbE1EjTRNUaYu0iRDHDIZOdiGj05jidiXEDk911IdZghiMXALeBZIJIIR0Ot4aO+10TfNxlijnhY+NAkk3EEN367pTqlJ2CHlxzow0e8MYB7G8K0IF765iLm513Dy5BvobrZhHzoYDVN4QxuhYyKMYkpRTus2+u0+LDPB4X6MOCKW+bS+6aLd6hMqE11yvLXVxdLiLSzNL6O920OnZWJ32yTdxN6OiQ7t67RttHYs0nvYJ6ct2hP4MTuECmsslZIKsgmtatguKTVVRTmupCJIVkc2RzqFnBHLHhHiRdwtbKgJGR80YXCKUv1Oi86pXpuwPF0KTh7jhtvFvLMHO3Cwf9BGLxggcLq4ueng9taQyqloKqFStiVFVKqoJIyKSUkDrDgdmKmPy+YaHrv8Ll5ZOotr9g6e/O0jnLhxDguDNp74/mM88/NpJKlASVVR2Bz6VF1y2YBgf723DOOHtzC38gV+6v2NRy+9g5eWzuBKfwOPkP70n5/gj+EdPH71PTx17UOsDgZ4/qvzOPHdj+gELj7vLOLLzhJCkZFDun63tvHs9VP4YOtXLIxaeO76p3h/7RL+otBfXPgMb69exKp/iJcXzuDNm+exEZp4YfkUXr91DusB6fOn8eriWVhZqMtG5VDqzKoA7tErbnyaSDUNEtQ6tDET9ZiNRjdyoVmO0qlSoeRygtml1utjddnYKJaTjHqU+lRNFUISxLGqNZ4uGU0SnihBEGq90nuiOFHTZTxxlE7ouWIML4mRJKlCVlDxJqJQzPFGLmRVzFP6caIcDkNfIeCLHYYiVzm516Hqjv8hhp/GKhzOLbdYVgqaf7jn9AdwyAj9IJiJUA/VB0ToJpHKITsRhCR9WIRuHCKKIoVQVExKrvtTTZdKT5uyvGsiHSfGKA4mDgtyGJa5KmYdppw4nTYaj/uZDuMo1GXDxUpIRJFP5uC0TOYlpaDItFTVDIeh6xApumxKmjxFkmjG71M2/OXjDxpLKWbkMIq4CzKNSiEs7kI4Dnt6OotCy0yETuBNyoa/gEmeHYuQu0gUWhjtfxA6MecwaVimfwI0jXXZ3L9Talk3pMwKmRDxR1x3ilT/th6mDv8FaT5GizcARZgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Dashboard showing full recovery after both quotas reset: daily quota at 30/40 tokens, weekly quota at 86/100 tokens, with requests succeeding again after the 20-minute weekly reset&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/ab420098fb482176682fb29c2c6fdbe1/3c492/scenario-3-finished-dashboard.png&quot;
        srcset=&quot;/static/ab420098fb482176682fb29c2c6fdbe1/ff46a/scenario-3-finished-dashboard.png 325w,
/static/ab420098fb482176682fb29c2c6fdbe1/a6d36/scenario-3-finished-dashboard.png 650w,
/static/ab420098fb482176682fb29c2c6fdbe1/3c492/scenario-3-finished-dashboard.png 1300w,
/static/ab420098fb482176682fb29c2c6fdbe1/d8104/scenario-3-finished-dashboard.png 1365w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Key observations from the k6 log output:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;log&quot;&gt;&lt;pre class=&quot;language-log&quot;&gt;&lt;code class=&quot;language-log&quot;&gt;&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0305&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Daily quota reset detected:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;39&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0609&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Daily quota reset detected:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;38&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0914&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Daily quota reset detected:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;13&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;39&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0995&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Weekly quota low:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt; tokens remaining
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1056&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Weekly quota low:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; tokens remaining
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1076&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Request made during EXHAUST&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;429&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1076&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; Weekly quota exhausted &lt;span class=&quot;token operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;17&lt;/span&gt; minute mark&lt;span class=&quot;token operator&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1076&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;CASCADING LIMIT DETECTED:&lt;/span&gt; Weekly blocks despite &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt; daily tokens available
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1117&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;CASCADING LIMIT DETECTED:&lt;/span&gt; Weekly blocks despite &lt;span class=&quot;token number&quot;&gt;23&lt;/span&gt; daily tokens available
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1267&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Daily quota reset detected:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;23&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;38&lt;/span&gt;
&lt;span class=&quot;token level info keyword&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1267&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;Weekly quota reset detected:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;98&lt;/span&gt; tokens available&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The log reveals several critical moments:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Multiple daily resets&lt;/strong&gt;: At around 5, 10, and 15 minutes, the daily quota resets (16→39, 8→38 and 13→39) while requests continue to succeed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Weekly exhaustion&lt;/strong&gt;: At 1076 (≈18 minutes), weekly quota hits zero and requests begin failing with 429 status&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cascading enforcement&lt;/strong&gt;: At 1076 and 1117 (≈18-19 minutes), the system explicitly detects that weekly quota is blocking requests despite daily quota having 23-24 tokens available - this is the key demonstration of hierarchical enforcement&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Full recovery&lt;/strong&gt;: At 1267 (≈21 minutes), both quotas reset and requests succeed again&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Key Learning&lt;/strong&gt;: The hierarchical limit enforcement ensures that weekly quota acts as the ultimate cost ceiling. Even when daily quota resets, exhausted weekly quota continues to block requests. This prevents sophisticated attacks where users try to game the system by timing requests around daily reset windows.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;This hands-on implementation demonstrates how to build rate limiting for GenAI APIs with multiple protection layers:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Architectural Takeaways&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Multi-level protection&lt;/strong&gt; is essential: minute-level rate limiting handles bursts, daily quotas prevent sustained abuse, and weekly quotas provide ultimate cost ceilings&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hierarchical enforcement&lt;/strong&gt; prevents gaming: all layers must approve a request, making it impossible to exploit reset timing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimistic locking with refunds&lt;/strong&gt; enables accurate cost tracking even when exact costs are only known post-generation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redis-backed state&lt;/strong&gt; provides atomic operations and distributed consistency for production deployments&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Implementation Insights&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Token bucket continuous refill is more granular than discrete intervals, improving user experience during legitimate bursts&lt;/li&gt;
&lt;li&gt;Automatic quota resets reduce operational overhead - no manual intervention needed&lt;/li&gt;
&lt;li&gt;Background metrics collection enables real-time observability without impacting request performance&lt;/li&gt;
&lt;li&gt;Cost estimation with true-up accounting (provisional reservation + correction) handles unpredictable GenAI costs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The complete project code, including FastAPI implementation, Redis storage layer, k6 load tests, and dashboard visualization, is available at &lt;a href=&quot;https://github.com/handsonarchitects/denial-of-wallet-demo&quot;&gt;https://github.com/handsonarchitects/denial-of-wallet-demo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We encourage you to clone the repository, run the scenarios, and experiment with different configurations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Adjust bucket capacity and refill rates to see burst behavior change&lt;/li&gt;
&lt;li&gt;Modify quota windows to match your business requirements&lt;/li&gt;
&lt;li&gt;Add new scenarios to test edge cases specific to your application&lt;/li&gt;
&lt;li&gt;Extend the implementation with per-user quotas, priority tiers, or cost-based pricing&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;final-thoughts&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#final-thoughts&quot; aria-label=&quot;final thoughts permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Building cost-aware rate limiting is no longer optional for production GenAI applications. The unpredictable cost variability of agentic workflows, combined with the ease of programmatic access, makes Denial of Wallet attacks a real and present risk. Start with the patterns demonstrated in this series, measure your actual usage, and tune the limits to protect your budget while serving legitimate users effectively.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Denial of Wallet: Cost-Aware Rate Limiting for Generative AI Applications - Strategy (Part 2)]]></title><description><![CDATA[This is Part 2 of the Denial of Wallet series. Part 1 introduced DoW as a distinct failure mode and explained why traditional DoS…]]></description><link>https://handsonarchitects.com/blog/2025/denial-of-wallet-cost-aware-rate-limiting-part-2/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2025/denial-of-wallet-cost-aware-rate-limiting-part-2/</guid><pubDate>Wed, 10 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/764cd7d0d1c7451004199734279cc50d/71c1d/denial-of-wallet-2-hero.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAADJElEQVR42gEZA+b8AP/79P779Pz897y8uK+uq66tqbSyrra0sLW0sLW0sLSzr7e1sbSzr7GwrLW0sLWzr7a0sLKxraakoM3LwwD++/P///7n5uKXkYza1M/6+PLNy8e8vLm9vbm+vbrKycXEw7/X1dHc2dXT0c3NzMjRz8vt6uXd2tW0sKkA//32wLu1clZFXjcigG5i6+3ryszMw8TAuLe0vr25v7+7np+dra2qxcTBqqqosrOwrK+t3+Dg7+7subWuAO/t5lg/M5loRKl6UqifmtfFn9WqWdza1/Dv6+De2fLw7P///Orp5t/c1/Tz77CuqXlrXZqWkLy8uLq2rwD49u6NaU+ifF67mn7Pv7PJu5qkgjzo5d/Bv7unpqTLysb//PXGxcGqqabExsSxoJDQkF2PZ0YlLDG7tq8A///4x7OgXUMzel9Ng2hUx8bDtLm6//330tPQwcG97enk8Ozmrq6rwsC8q6yqrZeCuqONkHpmT0U7vLmyAP7/+sSzpmU7JUAqJFxRTM28rMmxnb3ExF2BjaCrrP//9/Lv6MvIw8PDv8nJxsGVcZtnQcqNXsmxnry7tQDiwqvIVROwVSB5QCCkYTvFm3urf1u6v71QbnjEysj///f79/ClsrYjWXV3lKG6u7RyVEBhd3uKprG8tKoAyINbv0wN3mEc42UezFUTxnI548Cg9vj3x8jF+fXu+vXt4tXIr6eeu768zM/NSIuoK3OTLoWtJHmfo6qoALl8U8+CS8Z4RN9tKrRfMKVoO9W7pNzc2tzX0evm3+Lf2Jx9ZLd5SaennWqgtih8oTGHrmGCiit5m5SwuADVvqzYcDDph0bMdzuheV6cn5yrpp6UkYySkIuuqaGuqqOoopm3oIp1kZdSkKtNiaJugoGwimkmeZ6cucIAqbO3RT0/dks2TzsymZua/////Prx//72//72//zz//zy//70///4/Pnv///2lKChUkxCNVdjQmNx+PPqAHmQnwAuYUNtiAhCakhvif/88Pz58fz48P348f358f358f358f358Pr37v///3F0chEfJiErLzQ3N+7r4oZtCpYBVQImAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cost-aware rate limiting strategies for GenAI applications&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/764cd7d0d1c7451004199734279cc50d/3c492/denial-of-wallet-2-hero.png&quot;
        srcset=&quot;/static/764cd7d0d1c7451004199734279cc50d/ff46a/denial-of-wallet-2-hero.png 325w,
/static/764cd7d0d1c7451004199734279cc50d/a6d36/denial-of-wallet-2-hero.png 650w,
/static/764cd7d0d1c7451004199734279cc50d/3c492/denial-of-wallet-2-hero.png 1300w,
/static/764cd7d0d1c7451004199734279cc50d/71c1d/denial-of-wallet-2-hero.png 1536w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is Part 2 of the Denial of Wallet series. &lt;a href=&quot;https://handsonarchitects.com/blog/2025/denial-of-wallet-cost-aware-rate-limiting-part-1/&quot;&gt;Part 1&lt;/a&gt; introduced DoW as a distinct failure mode and explained why traditional DoS protections leave your budget exposed (recap: &lt;em&gt;traditional rate limiting protects your infrastructure; cost-aware rate limiting protects your budget; you need both&lt;/em&gt;). This post compares four rate limiting algorithms for cost-aware protection, analyzes trade-offs specific to Generative AI workloads, and provides a practical checklist for implementation. Part 3 will deliver a hands-on implementation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;introduction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#introduction&quot; aria-label=&quot;introduction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Introduction&lt;/h2&gt;
&lt;p&gt;You&apos;re building an application that uses Generative AI models through an &lt;a href=&quot;#agentic-architecture-note&quot;&gt;agentic architecture&lt;/a&gt;. Your application is exposed to end users via web or mobile interfaces. We will study scenarios where Generative AI model usage is expensive and unpredictable. Cost varies widely based on input size, model used, agent workflow complexity, and the number of tool calls executed. Compare this to traditional web applications where the cost per request is relatively stable. Serving an HTML page or handling a REST API call consumes predictable resources. Even serverless functions have relatively predictable execution time and cost within bounded input ranges.&lt;/p&gt;
&lt;p&gt;In GenAI applications, one user request might cost $0.001 (cached response) while another costs $0.50 (multi-step agentic workflow with GPT-4o and multiple tool invocations). Both count as &quot;one request&quot; from a traditional rate limiting perspective. This cost unpredictability makes your application vulnerable to Denial of Wallet attacks. Malicious users (or even curious legitimate users) can exploit cost variability to drive up bills or exhaust budgets. In other words, treating all requests equally (for example, by computing average request cost) is insufficient because the standard deviation of cost per request is very high. Treating all requests equally exposes you to the risk of high bills from an attacker who figures out how to trigger the most expensive requests repeatedly. A key challenge is that exact request costs are only known after LLM responses complete, requiring provisional reservation and true-up accounting.&lt;/p&gt;
&lt;p&gt;This post focuses on practical mitigation strategies. You&apos;ll learn how to choose the right rate limiting algorithm for your GenAI workload, how to gather the data you need before implementing controls, and how to roll out cost-aware limits safely. Think of this as an Architecture Decision Record (ADR) combined with actionable checklists. By the end, you&apos;ll know what questions to ask, what trade-offs to consider, and how to implement cost-aware rate limiting without breaking legitimate user workflows.&lt;/p&gt;
&lt;h2 id=&quot;rate-limiting-algorithm-options-for-cost-aware-protection&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rate-limiting-algorithm-options-for-cost-aware-protection&quot; aria-label=&quot;rate limiting algorithm options for cost aware protection permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rate Limiting Algorithm Options for Cost-Aware Protection&lt;/h2&gt;
&lt;p&gt;Let&apos;s compare four rate limiting algorithms in the context of cost-aware rate limiting for Generative AI applications, where preventing Denial of Wallet attacks is critical. Each algorithm offers different trade-offs between burst handling, predictability, implementation complexity, and resource overhead.&lt;/p&gt;
&lt;p&gt;When evaluating these algorithms, remember that we&apos;re throttling cost consumption rather than just request counts. A request that triggers a complex agentic workflow with multiple LLM calls should consume more of the user&apos;s quota than a simple cached lookup. The algorithm you choose must support this cost-based accounting while still providing good user experience and operational characteristics.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Terminology
To avoid confusion, we use the following terms consistently throughout this post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Budget units&lt;/strong&gt; (or &lt;strong&gt;credits&lt;/strong&gt;): The quota tracked by the rate limiter. For simplicity, we use a 1-to-1 mapping where 1 budget unit = $0.001 (one-tenth of a cent). A $0.50 request consumes 500 budget units.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Model tokens&lt;/strong&gt;: The actual tokens processed by the LLM (input tokens + output tokens), as reported by the LLM API. For example, OpenAI&apos;s API returns &lt;code class=&quot;language-text&quot;&gt;prompt_tokens&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;completion_tokens&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;total_tokens&lt;/code&gt; in the &lt;code class=&quot;language-text&quot;&gt;usage&lt;/code&gt; metadata.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost (USD)&lt;/strong&gt;: The dollar amount charged by the LLM provider, calculated from model tokens using per-model pricing (e.g., $0.15 per 1M input tokens for GPT-4o).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The conversion flow is: &lt;strong&gt;model tokens&lt;/strong&gt; → &lt;strong&gt;cost (USD)&lt;/strong&gt; via pricing catalog → &lt;strong&gt;budget units&lt;/strong&gt; via the 1:0.001 ratio. When we say &quot;a request consumes 500 budget units,&quot; we mean it costs $0.50 in USD, regardless of how many model tokens were used.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;token-bucket-algorithm&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#token-bucket-algorithm&quot; aria-label=&quot;token bucket algorithm permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Token Bucket Algorithm&lt;/h3&gt;
&lt;p&gt;The Token Bucket algorithm maintains a bucket with a maximum capacity of budget units that refill at a constant rate. Incoming requests consume budget units from the bucket based on their cost (USD), and requests are rejected when insufficient budget units are available. This approach allows variable processing rates and can accommodate short-term traffic bursts (which could be triggered without user&apos;s knowledge/intention in agent workflows) while maintaining long-term rate limits.&lt;/p&gt;
&lt;p&gt;In a cost-aware context, each request consumes budget units proportional to its cost. A $0.001 request consumes 1 budget unit, while a $0.50 request consumes 500 budget units. The bucket refills at a steady rate (e.g., 1000 budget units per hour = approximately $1/hour budget).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note on model token consumption&lt;/strong&gt;: For GenAI applications, you may need dual limiting: request-based (requests per second) and model token-based (model tokens per second consumed by the LLM). Since exact model token counts are often only known post-response, consider pre-request estimation based on prompt length plus hard caps via &lt;code class=&quot;language-text&quot;&gt;max_tokens&lt;/code&gt; parameter to prevent unbounded generation costs. One technique used in practice is to calculate the cost upfront based on input model tokens, then estimate output model tokens using the &lt;code class=&quot;language-text&quot;&gt;max_tokens&lt;/code&gt; parameter (provisional reservation), and adjust at the end to reflect actual output model tokens used (correction). In practice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provisional reservation + correction:
&lt;ul&gt;
&lt;li&gt;at request start, calculate &lt;code class=&quot;language-text&quot;&gt;estimated_cost_usd = cost_usd(input_model_tokens) + cost_usd(max_tokens_estimate)&lt;/code&gt;, then convert to budget units: &lt;code class=&quot;language-text&quot;&gt;reserve_budget_units = estimated_cost_usd / 0.001&lt;/code&gt; - this ensures you don&apos;t exceed the budget upfront;&lt;/li&gt;
&lt;li&gt;once the response completes, calculate &lt;code class=&quot;language-text&quot;&gt;actual_cost_usd = cost_usd(input_model_tokens) + cost_usd(actual_output_model_tokens)&lt;/code&gt;, convert to &lt;code class=&quot;language-text&quot;&gt;actual_budget_units = actual_cost_usd / 0.001&lt;/code&gt;, and refund &lt;code class=&quot;language-text&quot;&gt;reserve_budget_units - actual_budget_units&lt;/code&gt; - this ensures accurate accounting.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Per-model pricing: different models have distinct input vs output token prices. Maintain a pricing catalog keyed by model and use model-specific rates when converting model tokens to cost (USD). See also provider docs on rate limits and model-specific quotas, e.g., &lt;a href=&quot;https://platform.claude.com/docs/en/api/rate-limits&quot;&gt;Anthropic rate limits&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Formula: &lt;code class=&quot;language-text&quot;&gt;reserve_budget_units = (cost_usd(input_model_tokens) + cost_usd(max_tokens_estimate)) / 0.001&lt;/code&gt;; &lt;code class=&quot;language-text&quot;&gt;actual_budget_units = (cost_usd(input_model_tokens) + cost_usd(actual_output_model_tokens)) / 0.001&lt;/code&gt;. Apply the delta to refund the difference.&lt;/li&gt;
&lt;li&gt;Example (assuming GPT-4o pricing: $2.50/1M input tokens, $10.00/1M output tokens):&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;estimate:
- input_model_tokens: 800
- max_tokens: 300
- estimated_cost_usd = (800 * 2.50/1M) + (300 * 10.00/1M) = $0.002 + $0.003 = $0.005
- reserve_budget_units = 0.005 / 0.001 = 5 budget units

model returns:
- actual_output_model_tokens: 120
- actual_cost_usd = (800 * 2.50/1M) + (120 * 10.00/1M) = $0.002 + $0.0012 = $0.0032
- actual_budget_units = 0.0032 / 0.001 = 3.2 budget units
- refund = 5 - 3.2 = 1.8 budget units&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provides granular control over cost consumption with configurable maximum capacity (budget ceiling) and refill rate (budget replenishment rate)&lt;/li&gt;
&lt;li&gt;Allows legitimate burst traffic to be processed immediately when budget units are available, improving user experience during occasional high-cost operations&lt;/li&gt;
&lt;li&gt;Offers flexibility to adapt to varying traffic patterns and dynamic workload requirements without rejecting expensive requests unnecessarily&lt;/li&gt;
&lt;li&gt;Simple conceptual model that is relatively straightforward to implement and understand, making it easier to explain to stakeholders&lt;/li&gt;
&lt;li&gt;Well-suited for scenarios where users occasionally need to exceed steady-state spending limits (e.g., triggering agentic workflow that requires multiple LLM calls)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Requires careful tuning of bucket capacity and refill rate to prevent DoW attack vectors while still allowing legitimate usage patterns. If done poorly:
&lt;ul&gt;
&lt;li&gt;Can be exploited by greedy users who consume all available budget units during bursts, potentially causing cost spikes if bucket capacity is set too high&lt;/li&gt;
&lt;li&gt;May lead to unpredictable monthly cost patterns in Generative AI contexts where accumulated budget units enable sudden resource consumption&lt;/li&gt;
&lt;li&gt;Does not provide strict cost guarantees as accumulated budget units enable users to spend their entire daily budget in minutes if they&apos;ve been idle&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Requires additional computational overhead for budget unit generation and bucket management operations, though this is typically negligible compared to AI inference costs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;When to use&lt;/strong&gt;: Token Bucket works well when your users have legitimate use cases for occasional bursts above their steady-state rate (know your agentic workflow!). For example, a user might invoke multiple agentic workflows by sending complex, multi-step requests. The burst tolerance makes the system feel responsive rather than artificially constraining legitimate workflows.&lt;/p&gt;
&lt;h3 id=&quot;leaky-bucket-algorithm&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#leaky-bucket-algorithm&quot; aria-label=&quot;leaky bucket algorithm permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Leaky Bucket Algorithm&lt;/h3&gt;
&lt;p&gt;The Leaky Bucket algorithm queues incoming requests in a fixed-capacity bucket and processes them at a constant rate, like water leaking from a bucket (think of it as a FIFO queue with fixed capacity). Requests exceeding bucket capacity are rejected or delayed. However, for real-time applications like chat interfaces, delaying requests may not make sense, so rejection is preferred. This enforces a steady, predictable output rate regardless of input traffic variability.&lt;/p&gt;
&lt;p&gt;In cost-aware implementations, the &quot;leak rate&quot; represents the steady cost budget consumption rate. This smooths cost consumption over time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ensures constant, predictable cost consumption rate that directly translates to predictable monthly bills for AI applications&lt;/li&gt;
&lt;li&gt;Smooths out bursty traffic patterns by enforcing steady processing, preventing sudden cost spikes that trigger finance alerts&lt;/li&gt;
&lt;li&gt;Provides straightforward behavior that is easier to maintain and debug compared to more complex algorithms&lt;/li&gt;
&lt;li&gt;Helps mitigate both Denial of Service and Denial of Wallet attacks through strict rate enforcement at the cost dimension (assuming we can estimate LLM output model tokens reliably)&lt;/li&gt;
&lt;li&gt;Ensures fair resource distribution when implemented with per-user (or per-tenant) queues; each queue processes requests in FIFO order, preventing any single user from dominating the shared budget. Note: a single global queue can cause head-of-line blocking where one noisy user starves others&lt;/li&gt;
&lt;li&gt;Excellent for aligning technical cost controls with financial planning cycles (e.g., $X per hour leak rate = $24X per day budget)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Limited flexibility in adjusting to legitimate traffic pattern variations or seasonal demand without manual intervention&lt;/li&gt;
&lt;li&gt;Cannot quickly process small bursts of legitimate requests that exceed the constant leak rate, potentially frustrating users&lt;/li&gt;
&lt;li&gt;Requires additional computational overhead to manage the request queue and timing mechanisms&lt;/li&gt;
&lt;li&gt;May struggle to handle very short-lived bursts that exceed bucket capacity, leading to request rejections for legitimate use cases&lt;/li&gt;
&lt;li&gt;Strictly enforces rate limits which can negatively affect user experience during legitimate usage spikes (e.g., urgent batch processing)&lt;/li&gt;
&lt;li&gt;Choosing optimal bucket size (queue depth) and leak rate parameters can be complex and requires understanding usage patterns&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;When to use&lt;/strong&gt;: Leaky Bucket is ideal when cost predictability is paramount. For example, if your AI workflow runs in a background or asynchronous manner where strict cost controls are necessary, Leaky Bucket provides the tightest cost controls. The trade-off is reduced flexibility for users, so it does not suit scenarios where users interact with the system in real time and expect responsiveness (for example, via chat interfaces).&lt;/p&gt;
&lt;h3 id=&quot;fixed-window-algorithm&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fixed-window-algorithm&quot; aria-label=&quot;fixed window algorithm permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Fixed Window Algorithm&lt;/h3&gt;
&lt;p&gt;The Fixed Window algorithm divides time into fixed intervals (windows) and limits requests to a specific cost budget per window. When the budget is exceeded, requests are rejected until the next window begins. This approach provides simple, time-based rate limiting with minimal state tracking.&lt;/p&gt;
&lt;p&gt;In a cost-aware implementation, each window has a cost budget (e.g., $10 per hour). Requests consume budget from the current window. When the budget is exhausted, subsequent requests are rejected until the window resets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Extremely simple to implement with minimal computational and memory overhead, reducing operational complexity&lt;/li&gt;
&lt;li&gt;Provides clear, predictable cost windows that align well with billing cycles for AI APIs (hourly, daily, monthly)&lt;/li&gt;
&lt;li&gt;Low resource consumption makes it suitable for high-scale distributed systems handling millions of users&lt;/li&gt;
&lt;li&gt;Easy to reason about and debug due to straightforward window reset logic, simplifying troubleshooting&lt;/li&gt;
&lt;li&gt;Good performance characteristics for stable, consistent traffic patterns with predictable user behavior&lt;/li&gt;
&lt;li&gt;Can be implemented efficiently in distributed systems using simple counters in Redis or similar key-value stores&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Suffers from boundary burst problem where users can make 2x budget requests by timing requests at window edges (e.g., $10 at 09:59 and $10 at 10:01)&lt;/li&gt;
&lt;li&gt;Can lead to unpredictable cost spikes when traffic concentrates at window boundaries, defeating the purpose of cost controls&lt;/li&gt;
&lt;li&gt;Not well-suited for variable or bursty traffic patterns (common in production GenAI environments)&lt;/li&gt;
&lt;li&gt;Window reset behavior can create sudden availability changes that impact user experience (budget available → budget unavailable at window boundary)&lt;/li&gt;
&lt;li&gt;Ineffective at preventing DoW attacks that exploit window boundary vulnerabilities through coordinated timing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;When to use&lt;/strong&gt;: Fixed Window is appropriate when simplicity and operational efficiency are priorities and your usage patterns are relatively stable. It&apos;s a good starting point for MVPs or internal tools where the boundary burst problem is acceptable. However, once you identify real usage patterns, you may need to migrate to a more sophisticated algorithm to prevent DoW attacks effectively.&lt;/p&gt;
&lt;h3 id=&quot;sliding-window-algorithm&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sliding-window-algorithm&quot; aria-label=&quot;sliding window algorithm permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Sliding Window Algorithm&lt;/h3&gt;
&lt;p&gt;The Sliding Window algorithm maintains a continuously moving time window and tracks requests within that rolling interval, providing more accurate rate limiting than fixed windows. The classic implementation (Sliding Window Log) tracks individual request timestamps and costs. The memory-optimized variant (Sliding Window Counter) uses weighted counts from fixed windows to reduce memory overhead while approximating the sliding behavior.&lt;/p&gt;
&lt;p&gt;In cost-aware implementations, the sliding window tracks total cost consumed within the rolling time period. Each request&apos;s cost is recorded with its timestamp. As time advances, costs outside the window are discarded.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sliding Window Log&lt;/strong&gt; fully eliminates boundary burst issues by tracking exact timestamps, providing perfect cost distribution accuracy&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sliding Window Counter&lt;/strong&gt; mostly eliminates boundary bursts through weighted approximation while using significantly less memory&lt;/li&gt;
&lt;li&gt;Ensures fairness by continuously monitoring cost consumption rather than resetting at arbitrary intervals&lt;/li&gt;
&lt;li&gt;Adapts well to varying traffic patterns, making it suitable for unpredictable AI workload demands&lt;/li&gt;
&lt;li&gt;Provides more accurate rate limiting that better prevents DoW attacks compared to fixed windows&lt;/li&gt;
&lt;li&gt;Better user experience through consistent cost enforcement without sudden window resets that surprise users&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Significantly more complex to implement, debug, and maintain compared to simpler algorithms, increasing development time&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sliding Window Log&lt;/strong&gt; requires substantial memory (O(requests in window) per user) and CPU for timestamp management at scale&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sliding Window Counter&lt;/strong&gt; uses approximation that allows small residual boundary bursts (often single-digit percent in practice, depending on window size and weighting scheme)&lt;/li&gt;
&lt;li&gt;Scalability challenges in distributed systems requiring synchronization across multiple nodes to maintain consistency&lt;/li&gt;
&lt;li&gt;Requires more sophisticated infrastructure and state management for production deployment (e.g., sorted sets in Redis for Log variant, atomic counters for Counter variant)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;When to use&lt;/strong&gt;: Sliding Window is a good choice when DoW prevention is critical, you need accurate and fair cost enforcement, and the implementation complexity is justified to protect your budget. Use the memory-optimized variant (Sliding Window Counter) for large-scale deployments.&lt;/p&gt;
&lt;h3 id=&quot;selection-criteria-for-generative-ai-applications&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#selection-criteria-for-generative-ai-applications&quot; aria-label=&quot;selection criteria for generative ai applications permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Selection Criteria for Generative AI Applications&lt;/h3&gt;
&lt;p&gt;When choosing a rate limiting algorithm for cost-aware DoW prevention in Generative AI applications, consider these dimensions:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cost Predictability&lt;/strong&gt;
How important is it that monthly costs stay within strict bounds? Leaky Bucket and Sliding Window provide better cost predictability than Token Bucket. Fixed Window provides predictability within each window but suffers from boundary bursts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Burst Tolerance&lt;/strong&gt;
Do your users have legitimate use cases for occasional bursts above steady-state rates? Token Bucket allows controlled bursts. Leaky Bucket strictly enforces steady consumption. Sliding Window and Fixed Window fall in between.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Implementation Resources&lt;/strong&gt;
What engineering resources can you allocate to building and maintaining the rate limiter? Fixed Window offers the simplest implementation. Sliding Window requires the most infrastructure and engineering effort.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Attack Surface&lt;/strong&gt;
How sophisticated are the DoW attack vectors you need to defend against? Sliding Window and Leaky Bucket offer better protection than Fixed Window and Token Bucket. Fixed Window is particularly vulnerable to boundary timing exploits.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scale Requirements&lt;/strong&gt;
How many users and requests must you support? Fixed Window and memory-optimized Sliding Window Counter scale best. Classic Sliding Window Log has higher memory requirements per user.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;User Experience&lt;/strong&gt;
How important is it to maintain smooth, responsive user experience during legitimate usage spikes? Sliding Window and Token Bucket provide smoother user experience than strict enforcement algorithms. Leaky Bucket and Fixed Window can create frustrating rejection patterns.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Recommendation&lt;/strong&gt;: If implementation complexity is a concern, start with Fixed Window to get cost controls in place quickly. Once you understand your usage patterns better, migrate to more sophisticated algorithms like Sliding Window Counter.&lt;/p&gt;
&lt;h2 id=&quot;trade-offs-analysis-from-theory-to-implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#trade-offs-analysis-from-theory-to-implementation&quot; aria-label=&quot;trade offs analysis from theory to implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Trade-Offs Analysis: From Theory to Implementation&lt;/h2&gt;
&lt;p&gt;Choosing the right algorithm is only part of the solution. Before you write any code, you need to understand your system&apos;s cost characteristics, usage patterns, and business constraints. This section provides a practical 7-step checklist for implementing cost-aware rate limiting in production GenAI applications.&lt;/p&gt;
&lt;p&gt;Think of this as the path from &quot;we need to control costs&quot; to &quot;we have cost-aware rate limiting running in production.&quot; Each step builds on the previous one.&lt;/p&gt;
&lt;h3 id=&quot;step-1-build-cost-observability-early&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-1-build-cost-observability-early&quot; aria-label=&quot;step 1 build cost observability early permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 1: Build Cost Observability Early&lt;/h3&gt;
&lt;p&gt;Start tracking cost metrics from day one. Even if you don&apos;t have production traffic yet, simulated workloads and exploratory tests provide valuable insights. You cannot protect costs you cannot measure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Action items&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Instrument your application to track cost per request. Tag each LLM API call, vector search operation, and expensive computation with its cost.&lt;/li&gt;
&lt;li&gt;Export cost metrics to your observability platform (Prometheus, Datadog, CloudWatch, etc.). Create time-series metrics for:
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;request_cost_usd&lt;/code&gt; (histogram): Distribution of request costs, enabling p50/p95/p99 analysis and anomaly detection&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;total_spend_usd&lt;/code&gt; (counter with labels for user/tenant): Monotonically increasing total spend; derive hourly/daily rates via recording rules or range queries&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;expensive_operation_count&lt;/code&gt; (counter): Count of operations exceeding threshold (e.g., &gt;$0.10)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why this matters&lt;/strong&gt;: Without observability, you&apos;re flying blind. You won&apos;t know if your rate limiter is working. You won&apos;t know which users to exempt from strict limits. You won&apos;t know when to adjust thresholds. Invest in observability before you invest in controls.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Common pitfall&lt;/strong&gt;: Tracking only aggregate costs. You need per-operation granularity to identify DoW attack patterns and tune limits appropriately.&lt;/p&gt;
&lt;h3 id=&quot;step-2-measure-or-estimate-baseline-costs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-2-measure-or-estimate-baseline-costs&quot; aria-label=&quot;step 2 measure or estimate baseline costs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 2: Measure or Estimate Baseline Costs&lt;/h3&gt;
&lt;p&gt;When running Generative AI workloads, you likely won&apos;t have historical cost data yet. Start by estimating costs based on model pricing and expected usage patterns. Perform synthetic tests to validate your hypotheses. Use cost observability tools to gather real data as soon as possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Action items&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Do not focus on averages alone&lt;/strong&gt;. Understand the distribution of costs: p50, p90, p95, p99. The long tail matters.&lt;/li&gt;
&lt;li&gt;Create cost profiles for different request types:
&lt;ul&gt;
&lt;li&gt;Cached responses: $0.0001 - $0.001&lt;/li&gt;
&lt;li&gt;Simple LLM calls (small prompts): $0.01 - $0.05&lt;/li&gt;
&lt;li&gt;Complex agentic workflows: $0.20 - $0.50&lt;/li&gt;
&lt;li&gt;Batch processing with large documents: $1.00 - $5.00&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Identify the cost distribution across different models, input sizes, and request types. Which operations dominate your spend?&lt;/li&gt;
&lt;li&gt;Run synthetic load tests that simulate different user behaviors. Include both &quot;normal&quot; users and &quot;expensive&quot; users to understand the cost range.&lt;/li&gt;
&lt;li&gt;Document your findings. Create a table like this:&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation Type&lt;/th&gt;
&lt;th&gt;Avg Cost&lt;/th&gt;
&lt;th&gt;p95 Cost&lt;/th&gt;
&lt;th&gt;% of Total Requests&lt;/th&gt;
&lt;th&gt;% of Total Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cached lookup&lt;/td&gt;
&lt;td&gt;$0.0001&lt;/td&gt;
&lt;td&gt;$0.0001&lt;/td&gt;
&lt;td&gt;60%&lt;/td&gt;
&lt;td&gt;1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simple Q&amp;#x26;A&lt;/td&gt;
&lt;td&gt;$0.02&lt;/td&gt;
&lt;td&gt;$0.05&lt;/td&gt;
&lt;td&gt;30%&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agentic workflow&lt;/td&gt;
&lt;td&gt;$0.30&lt;/td&gt;
&lt;td&gt;$0.80&lt;/td&gt;
&lt;td&gt;9%&lt;/td&gt;
&lt;td&gt;45%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch processing&lt;/td&gt;
&lt;td&gt;$2.00&lt;/td&gt;
&lt;td&gt;$8.00&lt;/td&gt;
&lt;td&gt;1%&lt;/td&gt;
&lt;td&gt;44%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Why this matters&lt;/strong&gt;: You cannot set reasonable budgets without understanding baseline costs. The distribution (especially the long tail at p95/p99) determines how much burst capacity to allow. If p95 is 10x the average, Fixed Window or Leaky Bucket might reject too many legitimate requests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Common pitfall&lt;/strong&gt;: Optimizing for the average case. In GenAI applications, the p95 and p99 costs often drive your budget exposure. A few expensive requests can dominate total spend.&lt;/p&gt;
&lt;h3 id=&quot;step-3-identify-high-cost-users-and-patterns&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-3-identify-high-cost-users-and-patterns&quot; aria-label=&quot;step 3 identify high cost users and patterns permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 3: Identify High-Cost Users and Patterns&lt;/h3&gt;
&lt;p&gt;Once you have cost observability running, invest in exploratory analysis. Query your observability stack to find high-cost users and journeys. Understanding these patterns helps you optimize before implementing strict limits.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Action items&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sort users by total spend. Identify the top 10% of spenders. Are they legitimate power users or potential DoW attackers?&lt;/li&gt;
&lt;li&gt;Analyze their request patterns: burst size and frequency, time-of-day and day-of-week effects, model mix (which models are used most), tool-call frequency and patterns, cache hit rates, per-session vs per-user behavior trends&lt;/li&gt;
&lt;li&gt;For each high-cost pattern, ask: Could this be optimized?
&lt;ul&gt;
&lt;li&gt;Can we add caching to reduce redundant LLM calls?&lt;/li&gt;
&lt;li&gt;Can we batch requests to improve efficiency?&lt;/li&gt;
&lt;li&gt;Can we use smaller/faster models for certain operations?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Estimate potential savings and effort required for each optimization. Prioritize low-effort, high-impact optimizations before implementing rate limits.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why this matters&lt;/strong&gt;: Rate limiting is a defensive control. Optimization is an offensive strategy. If you can reduce costs through caching, batching, or model selection, you improve both user experience and budget protection. Implement optimizations first, then add rate limiting as a safety net.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Common pitfall&lt;/strong&gt;: Jumping straight to rate limiting without exploring optimization opportunities. You might throttle legitimate users when the real problem is an inefficient implementation.&lt;/p&gt;
&lt;h3 id=&quot;step-4-define-budgets-and-policies&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-4-define-budgets-and-policies&quot; aria-label=&quot;step 4 define budgets and policies permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 4: Define Budgets and Policies&lt;/h3&gt;
&lt;p&gt;Work with your finance team to translate business requirements into technical budgets. This is not purely an engineering decision. Budgets affect pricing, user experience, and revenue.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Action items&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Decide on budget dimensions:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Per-user budgets&lt;/strong&gt;: Each user gets $X per hour/day/month&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Per-session budgets&lt;/strong&gt;: Each login session gets $Y total spend&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Per-tenant budgets&lt;/strong&gt;: Enterprise customers get $Z per month&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Map budgets to product tiers:
&lt;ul&gt;
&lt;li&gt;Free tier: $0.50/day ($15/month)&lt;/li&gt;
&lt;li&gt;Pro tier: $5.00/day ($150/month)&lt;/li&gt;
&lt;li&gt;Enterprise tier: Custom limits negotiated per contract&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Set daily and monthly caps as safeguards. Even if your algorithm allows bursts, enforce hard caps to prevent runaway costs.&lt;/li&gt;
&lt;li&gt;Define overage policies:
&lt;ul&gt;
&lt;li&gt;Soft limit (warning to user): &quot;You&apos;ve used 80% of your daily budget&quot;&lt;/li&gt;
&lt;li&gt;Hard limit (request rejection): &quot;Daily budget exceeded. Budget resets in 4 hours.&quot;&lt;/li&gt;
&lt;li&gt;Grace period for accidental overages vs. immediate cutoff&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why this matters&lt;/strong&gt;: Technical controls must align with business policies. If you set budgets too low, you frustrate legitimate users and hurt retention. If you set budgets too high, you fail to prevent DoW attacks. Finance needs to approve these numbers because they directly impact revenue and costs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Common pitfall&lt;/strong&gt;: Setting budgets based only on engineering intuition. Involve finance and product teams to ensure budgets align with business model and user expectations.&lt;/p&gt;
&lt;h3 id=&quot;step-5-choose-your-rate-limiting-strategy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-5-choose-your-rate-limiting-strategy&quot; aria-label=&quot;step 5 choose your rate limiting strategy permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 5: Choose Your Rate Limiting Strategy&lt;/h3&gt;
&lt;p&gt;Based on the patterns you discovered, your estimates, and your application architecture, choose the most suitable rate limiting algorithm. This is an important architectural decision. Consider writing an Architecture Decision Record (ADR) to document your choice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Action items&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Review the algorithm comparison from the previous section (Token Bucket, Leaky Bucket, Fixed Window, Sliding Window).&lt;/li&gt;
&lt;li&gt;Consider trade-offs specific to your context:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Current infrastructure&lt;/strong&gt;: Do you already have Redis? Distributed counters? Or are you starting from scratch?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalability needs&lt;/strong&gt;: Are you handling 100 users or 100,000 users? Memory requirements differ significantly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implementation complexity&lt;/strong&gt;: Do you have time and resources to build Sliding Window, or do you need Fixed Window to ship quickly?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintenance burden&lt;/strong&gt;: Who will debug rate limiter issues at 2am? Simpler algorithms are easier to troubleshoot.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If you&apos;re uncertain, start with a simpler algorithm (Fixed Window) and plan to iterate. Ship cost controls quickly, gather data, then migrate to more sophisticated algorithms if needed.&lt;/li&gt;
&lt;li&gt;Document your decision in an ADR:
&lt;ul&gt;
&lt;li&gt;Context: What problem are you solving? What constraints do you face?&lt;/li&gt;
&lt;li&gt;Options considered: List 2-3 algorithms you evaluated&lt;/li&gt;
&lt;li&gt;Decision: Which algorithm did you choose and why?&lt;/li&gt;
&lt;li&gt;Consequences: What are the trade-offs? What might you need to revisit later?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Make the implementation configurable.&lt;/strong&gt; Use feature flags or configuration files to allow switching algorithms or tuning parameters without code changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why this matters&lt;/strong&gt;: Rate limiting algorithms have different operational characteristics. A poor choice increases maintenance burden or fails to prevent DoW attacks. Documenting your decision helps future engineers understand the rationale and constraints.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Common pitfall&lt;/strong&gt;: Over-engineering too early. You don&apos;t need perfect rate limiting from day one. You need good-enough rate limiting that protects your budget while you learn about real usage patterns. Iterate, iterate, iterate!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Distributed deployment consideration&lt;/strong&gt;: If you&apos;re running multiple application instances, you&apos;ll need centralized rate limiting (for example, Redis with Lua scripts for atomic operations). Local in-memory rate limiters in each instance will drift and fail to enforce global budgets correctly. Part 3 will cover distributed implementation patterns.&lt;/p&gt;
&lt;h3 id=&quot;step-6-extend-observability-and-alerting&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-6-extend-observability-and-alerting&quot; aria-label=&quot;step 6 extend observability and alerting permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 6: Extend Observability and Alerting&lt;/h3&gt;
&lt;p&gt;Once you&apos;ve chosen an algorithm and defined budgets, extend your observability to monitor rate limiter performance. You need visibility into both cost consumption and rate limiter behavior.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Action items&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set up alerts for spending anomalies:
&lt;ul&gt;
&lt;li&gt;Total daily spend crosses budget threshold&lt;/li&gt;
&lt;li&gt;Single request costs more than expected (e.g., &gt;$1.00)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Monitor rate limiter performance metrics:
&lt;ul&gt;
&lt;li&gt;Throttle rate: What percentage of requests are being rejected?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Create dashboards showing:
&lt;ul&gt;
&lt;li&gt;Current spend vs. budget (per user and globally)&lt;/li&gt;
&lt;li&gt;Throttled requests over time&lt;/li&gt;
&lt;li&gt;Distribution of request costs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Integrate cost alerts into your incident response workflow. Finance should be notified of budget overruns, not just engineering.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why this matters&lt;/strong&gt;: Rate limiters can fail silently if you don&apos;t monitor them. You might think you&apos;re protected while costs continue to climb because the rate limiter has a bug or is misconfigured. Observability catches these issues before they become expensive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Common pitfall&lt;/strong&gt;: Monitoring only request rejection rates. You also need to monitor budget consumption. A rate limiter that never rejects requests is not protecting your budget.&lt;/p&gt;
&lt;h3 id=&quot;step-7-optionally-start-with-read-only-mode&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-7-optionally-start-with-read-only-mode&quot; aria-label=&quot;step 7 optionally start with read only mode permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 7: Optionally Start with Read-Only Mode&lt;/h3&gt;
&lt;p&gt;If you already have production traffic and breaking user experience is a concern, consider starting with a &quot;read-only&quot; or &quot;shadow mode&quot; deployment. Track and log what would be throttled without actually rejecting requests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Action items&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Implement rate limiting logic but bypass enforcement. Log throttle decisions: &quot;User X would be throttled for request Y (cost $Z, budget consumed: $A/$B)&quot;.&lt;/li&gt;
&lt;li&gt;Run in shadow mode for 1-2 weeks to validate that your cost estimates and budgets are reasonable.&lt;/li&gt;
&lt;li&gt;Analyze the shadow mode logs:
&lt;ul&gt;
&lt;li&gt;How many requests would have been throttled?&lt;/li&gt;
&lt;li&gt;Are you throttling legitimate users or catching actual abuse?&lt;/li&gt;
&lt;li&gt;Do you need to adjust budgets or refine cost calculations?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Adjust thresholds based on real data before switching to enforcement mode.&lt;/li&gt;
&lt;li&gt;Prepare a mechanism for switching to enforcement mode when ready:
&lt;ul&gt;
&lt;li&gt;Feature flag controlled by configuration&lt;/li&gt;
&lt;li&gt;Emergency off-switch in case enforcement causes unexpected problems&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why this matters&lt;/strong&gt;: Turning on rate limiting in production is risky. If your budgets are too low or your cost calculations are wrong, you&apos;ll throttle legitimate users and hurt retention. Shadow mode lets you validate assumptions with real traffic before committing to enforcement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Common pitfall&lt;/strong&gt;: Skipping shadow mode and going straight to enforcement. This works if you have very high confidence in your cost models and budgets. For most teams, shadow mode reduces risk significantly.&lt;/p&gt;
&lt;h2 id=&quot;summary-from-theory-to-practice&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary-from-theory-to-practice&quot; aria-label=&quot;summary from theory to practice permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary: From Theory to Practice&lt;/h2&gt;
&lt;p&gt;You now have a framework for implementing cost-aware rate limiting:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Build observability first&lt;/strong&gt;: You cannot protect costs you cannot measure&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Understand your cost distribution&lt;/strong&gt;: Averages can be misleading. Focus on p95 and p99&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimize before limiting&lt;/strong&gt;: Caching and batching reduce costs without throttling users&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Align budgets with business goals&lt;/strong&gt;: Involve finance and product teams&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Choose an algorithm that fits your constraints&lt;/strong&gt;: Perfect is the enemy of shipped&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor rate limiter performance&lt;/strong&gt;: Track both budget consumption and throttle rates&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use shadow mode to de-risk rollout&lt;/strong&gt;: Validate assumptions before enforcement&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The best rate limiting strategy is the one you actually implement and maintain. Start simple. Gather data. Iterate.&lt;/p&gt;
&lt;h2 id=&quot;conclusion-from-theory-to-production&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion-from-theory-to-production&quot; aria-label=&quot;conclusion from theory to production permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion: From Theory to Production&lt;/h2&gt;
&lt;p&gt;You&apos;ve learned the theory behind cost-aware rate limiting and the practical steps to implement it. Let&apos;s recap the key takeaways:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Understanding the problem&lt;/strong&gt;: Generative AI applications introduce cost unpredictability that traditional rate limiting cannot address. One user request might cost $0.001 while another costs $0.50. Both count as &quot;one request&quot; from a DoS perspective, but they have wildly different budget impacts. Cost-aware rate limiting protects your budget by throttling based on cost consumption, not just request count.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Choosing the right algorithm&lt;/strong&gt;: Four algorithms offer different trade-offs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Token Bucket&lt;/strong&gt; allows bursts but can lead to cost spikes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leaky Bucket&lt;/strong&gt; provides strict cost predictability at the expense of flexibility&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fixed Window&lt;/strong&gt; is simple to implement but vulnerable to boundary exploits&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sliding Window&lt;/strong&gt; offers the best DoW protection with higher implementation complexity&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Generally - start with Fixed Window and iterate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Following a practical checklist&lt;/strong&gt;: Before you write code:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Build cost observability to measure what you want to protect&lt;/li&gt;
&lt;li&gt;Understand your cost distribution (p95 and p99 matter more than averages)&lt;/li&gt;
&lt;li&gt;Identify high-cost patterns and optimize before limiting&lt;/li&gt;
&lt;li&gt;Define budgets with finance and product teams&lt;/li&gt;
&lt;li&gt;Choose an algorithm that fits your constraints&lt;/li&gt;
&lt;li&gt;Extend observability to monitor rate limiter performance&lt;/li&gt;
&lt;li&gt;Use shadow mode to validate assumptions before enforcement&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Gathering data before committing&lt;/strong&gt;: The most important lesson: gather data and validate your assumptions. Run synthetic tests. Use shadow mode. Understand your users&apos; actual behavior before you throttle them. Rate limiting is a blunt instrument. Cost observability and optimization are your precision tools.&lt;/p&gt;
&lt;h2 id=&quot;whats-next&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#whats-next&quot; aria-label=&quot;whats next permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What&apos;s Next&lt;/h2&gt;
&lt;p&gt;This post covers the decision-making framework and practical steps for implementing cost-aware rate limiting. Part 3 of this series will provide a hands-on implementation.&lt;/p&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Piyush Garg. &quot;API Rate Limiting Strategies: Token Bucket vs. Leaky Bucket.&quot; Eraser.io, April 11, 2024. &lt;a href=&quot;https://www.eraser.io/decision-node/api-rate-limiting-strategies-token-bucket-vs-leaky-bucket&quot;&gt;https://www.eraser.io/decision-node/api-rate-limiting-strategies-token-bucket-vs-leaky-bucket&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&quot;Rate Limiting Algorithms - System Design.&quot; GeeksforGeeks. &lt;a href=&quot;https://www.geeksforgeeks.org/system-design/rate-limiting-algorithms-system-design/&quot;&gt;https://www.geeksforgeeks.org/system-design/rate-limiting-algorithms-system-design/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&quot;Sliding Window Rate Limiting and its Memory-Optimized Variant.&quot; RD Blog, February 5, 2024. &lt;a href=&quot;https://rdiachenko.com/posts/arch/rate-limiting/sliding-window-algorithm/&quot;&gt;https://rdiachenko.com/posts/arch/rate-limiting/sliding-window-algorithm/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;agentic-architecture-note&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#agentic-architecture-note&quot; aria-label=&quot;agentic architecture note permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Agentic architecture note&lt;/h3&gt;
&lt;p&gt;Assume agentic architecture is an application that consists of multiple LLM calls orchestrated to achieve a goal, such as a multi-step reasoning process.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Denial of Wallet: Cost-Aware Rate Limiting for Generative AI Applications - Introduction (Part 1)]]></title><description><![CDATA[This post introduces a series about Denial of Wallet (DoW) as a distinct failure mode from Denial of Service (DoS). In the first part, we'll…]]></description><link>https://handsonarchitects.com/blog/2025/denial-of-wallet-cost-aware-rate-limiting-part-1/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2025/denial-of-wallet-cost-aware-rate-limiting-part-1/</guid><pubDate>Fri, 21 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/c5e9b4b67262cc33026d70f3a3034db7/71c1d/denial-of-wallet-hero.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAADJElEQVR42gEZA+b8AP315f315P/+7+HZy56Pg5uGd7Kikv/76/304/315Pz05P/35+7n2HZ6dmptaKSln/Hw5P/76/315P725QD99eT+9+fPwrB6W0ZfNR5ULBZxSzHm18P/+ur88+L78+T//OrX1Mh4W0S5flKQZkViXFPIxrv//ez89OMA//fm8u7fv5Rwr3pRrohotpJznnlZ1bCQ9fbq//fl+/Pi//jn2tbLvamTs5J20KuLOisgdnt5///x+/LiAP/66ebh0uaoeadwR8qtkc2vlK+Sd9OUZ9qxkfXx4/304v/359PHtr6VdcCgg76rl1VAL7Osn///7/vz4gDy7d/Xp4HionGKalGAWD53W0V3WEGli3XjlF3Tt5/++uz//e/nzLKOXz19W0PpnWbJkmjy5tP/+en89OMA2cax651kwY5og2FNSyocUTguMCYhnUcXxXdFxbad2tPA2dLAz8e2iXVgkWBAf21cUoebucfE//bk/PTkAPHm1cCWdMpYFcBQEW09IWQ4I2M4IMRREsScf+no0crApZCOfsfBqv/y0HWFgi50kS+FqjiIqtnaz//76AD/+ens59nKhlvdWQ/HVxeeTR6xSQ/Pq5D09efHt5+sp5Sem4qhno3dzrFxmqMqkLxCj64wjLSqwML/++cA/PHf///50KaI3VIF7GMX3FsT4V8X5NfE+O/erYhpw7mihoZ5lpSFw7efo4duU4eaNYWmK3ucia22//nkAP/14vX3692JVu9YBediF+5eDtJkJu3m1//459LEsNnMsamkkru1ocW5ob59Uc6NYDt6kzOOtKq8vP/75wD/9uT28+WJbVvAURTYXRfNUQzCfFH69uj/9ubk387YzrPJwKrJwarg1Lm1sJxUa3AwbogyYXXf2sz/++kA//nn6eTWEkltBTxfKT1KGTRGSmt8//bl//fn3tfH0civ182z0siu6+HExMWtBRonGikyFR4kx8K3///uAP/+67zAugA6YxhPcLrAuxVOcQpDaN7Zzf/66fnw4Ozk1OXcy9zUwoyKflRWUXt6dWRlYgAFDp+el///77fK/ypUpiCiAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Architects find out the hard way about denial of wallet&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/c5e9b4b67262cc33026d70f3a3034db7/3c492/denial-of-wallet-hero.png&quot;
        srcset=&quot;/static/c5e9b4b67262cc33026d70f3a3034db7/ff46a/denial-of-wallet-hero.png 325w,
/static/c5e9b4b67262cc33026d70f3a3034db7/a6d36/denial-of-wallet-hero.png 650w,
/static/c5e9b4b67262cc33026d70f3a3034db7/3c492/denial-of-wallet-hero.png 1300w,
/static/c5e9b4b67262cc33026d70f3a3034db7/71c1d/denial-of-wallet-hero.png 1536w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This post introduces a series about &lt;strong&gt;Denial of Wallet&lt;/strong&gt; (DoW) as a distinct failure mode from Denial of Service (DoS). In the first part, we&apos;ll explore how to reason about cost-aware rate limiting across CDN, gateway, and application layers. After that, you&apos;ll learn when to invest in custom limits, &lt;a href=&quot;https://handsonarchitects.com/blog/2025/denial-of-wallet-cost-aware-rate-limiting-part-2/&quot;&gt;the second part&lt;/a&gt; focuses on user-aware limits and how to frame cost units and design strategies. Finally, part 3 of this series will provide a hands-on implementation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;introduction---the-cost-trap-in-ai-era-systems&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#introduction---the-cost-trap-in-ai-era-systems&quot; aria-label=&quot;introduction   the cost trap in ai era systems permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Introduction - The Cost Trap in AI-Era Systems&lt;/h2&gt;
&lt;p&gt;Six months after launching your AI-powered search feature, you get an urgent Slack message from finance: cloud costs have tripled. The operations team is confused. Your requests per second (RPS) metrics are steady. Latency is fine. No alerts fired. Everything looks healthy - except the bill.&lt;/p&gt;
&lt;p&gt;The problem? Your uniform rate limits protect performance but ignore cost variance. A basic search query costs you fractions of a cent in Elasticsearch time. An &lt;a href=&quot;#agentic-conversation-note&quot;&gt;agentic conversation&lt;/a&gt; with GPT-4o and multiple tool calls costs $0.20-$0.50. Both count as &quot;one request&quot; under your standard per-user 100 req/min limit. A single user can stay well within rate limits while running up hundreds of dollars in LLM charges.&lt;/p&gt;
&lt;p&gt;Traditional rate limiting was built for a simpler era when most requests had similar resource costs. In today&apos;s cloud-native systems (especially those integrating LLMs, vector databases, image generation, and third-party APIs) request costs vary by orders of magnitude (different number of tokens for LLM, different data shapes for database queries). Standard RPS-based throttles protect your infrastructure but leave your budget exposed.&lt;/p&gt;
&lt;p&gt;This post introduces &lt;strong&gt;Denial of Wallet&lt;/strong&gt; (DoW) as a distinct failure mode from Denial of Service (DoS). We&apos;ll explore how to reason about cost-aware rate limiting across CDN, gateway, and application layers. You&apos;ll learn when to invest in custom, user-aware limits and how to frame cost units and design strategies.&lt;/p&gt;
&lt;h3 id=&quot;why-now-aillm-adoption-and-usage-based-pricing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-now-aillm-adoption-and-usage-based-pricing&quot; aria-label=&quot;why now aillm adoption and usage based pricing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why Now? AI/LLM Adoption and Usage-Based Pricing&lt;/h3&gt;
&lt;p&gt;This problem has always existed with heterogeneous workloads - think image processing versus simple GETs. But LLM adoption has made it urgent. OpenAI charges $1.25 per million input &lt;a href=&quot;#llm-tokens-note&quot;&gt;tokens&lt;/a&gt; and $10.00 per million output tokens for GPT-4o. A single agentic workflow can easily consume 100,000 tokens, costing $0.20–$0.50 per request. Compare that to an Elasticsearch query at effectively near-zero cost per search after amortizing your cluster costs.&lt;/p&gt;
&lt;p&gt;The cost variance is staggering. In systems where cheap and expensive operations share the same rate limit buckets, a user can unknowingly (or deliberately) shift their request mix toward expensive operations. Your performance stays fine. Your spend explodes.&lt;/p&gt;
&lt;p&gt;To understand how to protect against this threat, we need to clearly distinguish between traditional performance-based attacks and cost-based exploitation. The key insight is that your existing DoS defenses (designed to protect availability and latency) operate on different metrics than what&apos;s needed to protect your budget. Let&apos;s examine how these two failure modes differ and why conventional rate limiting leaves you exposed to cost-based abuse.&lt;/p&gt;
&lt;p&gt;Consider a typical scenario. You deploy a chatbot that can answer questions from cached knowledge (cheap) or invoke an LLM agent with web search capabilities (expensive). Your API gateway enforces 100 req/min per user. Everything works as designed.&lt;/p&gt;
&lt;p&gt;Then a power user discovers they can run complex agentic queries repeatedly. They stay under 100 req/min. Your servers handle the load. But each query costs $0.40 in LLM fees. Over a month, this single user generates a $1,200 bill. Your rate limits did their job protecting availability. They just couldn&apos;t see the cost dimension.&lt;/p&gt;
&lt;h2 id=&quot;denial-of-service-vs-denial-of-wallet&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#denial-of-service-vs-denial-of-wallet&quot; aria-label=&quot;denial of service vs denial of wallet permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Denial of Service vs Denial of Wallet&lt;/h2&gt;
&lt;p&gt;Denial of Service attacks aim to exhaust system resources (CPU, memory, network bandwidth) making the service unavailable to legitimate users. Your CDN, WAF, and application rate limits are designed for this threat model. They cap requests per second, enforce queue depths, and shed load when close to saturation.&lt;/p&gt;
&lt;p&gt;Denial of Wallet attacks exploit a different vulnerability: the cost dimension. An attacker (or even a legitimate but cost-oblivious user) stays within your performance-oriented rate limits but selectively triggers expensive operations. Your infrastructure remains healthy. Your bill skyrockets.&lt;/p&gt;
&lt;h3 id=&quot;where-dos-controls-stop-and-dow-begins&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#where-dos-controls-stop-and-dow-begins&quot; aria-label=&quot;where dos controls stop and dow begins permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Where DoS Controls Stop and DoW Begins&lt;/h3&gt;
&lt;p&gt;Traditional DoS defenses operate at the infrastructure layer. Your CDN throttles suspicious IPs. Your API gateway enforces global or per-API-key RPS caps. Your load balancer monitors backend health. These controls protect availability and latency. They work beautifully when all requests cost roughly the same.&lt;/p&gt;
&lt;p&gt;But consider this scenario: your system serves both cached data lookups and live LLM inference. The cached lookup hits Redis and returns in 5ms at negligible cost. The LLM call waits 800ms for GPT-4o and costs $0.30. From a DoS perspective, the LLM call is slower but manageable - it&apos;s just one request in your queue. From a DoW perspective, it&apos;s 3,000x more expensive than the cache hit.&lt;/p&gt;
&lt;p&gt;Your CDN and gateway can&apos;t see this cost difference. They count requests and bytes. They don&apos;t know which code path each request will take or which third-party APIs you&apos;ll invoke. Cost context lives in your application layer - in the business logic that decides whether to serve from cache, hit the database, or call an LLM.&lt;/p&gt;
&lt;h3 id=&quot;cost-surfaces-llm-calls-vector-search-data-egress-image-processing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cost-surfaces-llm-calls-vector-search-data-egress-image-processing&quot; aria-label=&quot;cost surfaces llm calls vector search data egress image processing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Cost Surfaces: LLM Calls, Vector Search, Data Egress, Image Processing&lt;/h3&gt;
&lt;p&gt;Modern cloud-native systems expose multiple cost surfaces. Here are common examples where request costs vary dramatically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LLM API calls&lt;/strong&gt;: GPT-4o at $0.20–$0.50 per agentic conversation versus cached responses at $0.0001.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vector search&lt;/strong&gt;: Embedding generation ($0.0001 per query with a small model) versus brute-force similarity across millions of vectors (seconds of GPU time).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Image processing&lt;/strong&gt;: Thumbnail generation (milliseconds, cents) versus AI upscaling or video transcoding (minutes, dollars).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data egress&lt;/strong&gt;: Serving a 10KB JSON response versus streaming a 500MB dataset across regions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these can coexist in a single API surface. A user might invoke &lt;code class=&quot;language-text&quot;&gt;/api/search&lt;/code&gt; with a query parameter that triggers either a cheap cache hit or an expensive LLM fallback. The endpoint looks the same. The cost is not.&lt;/p&gt;
&lt;h3 id=&quot;comparison-dos-vs-dow&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#comparison-dos-vs-dow&quot; aria-label=&quot;comparison dos vs dow permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Comparison: DoS vs DoW&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Denial of Service (DoS)&lt;/th&gt;
&lt;th&gt;Denial of Wallet (DoW)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Primary goal&lt;/td&gt;
&lt;td&gt;Protect availability/latency&lt;/td&gt;
&lt;td&gt;Protect cost budget/spend risk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trigger pattern&lt;/td&gt;
&lt;td&gt;High RPS, resource exhaustion&lt;/td&gt;
&lt;td&gt;Normal RPS, high-cost request mix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Detection signals&lt;/td&gt;
&lt;td&gt;5xx, p95 latency, CPU saturation&lt;/td&gt;
&lt;td&gt;Bill/usage spike, per-user spend anomalies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Controls&lt;/td&gt;
&lt;td&gt;CDN/WAF throttles, queue backpressure&lt;/td&gt;
&lt;td&gt;Cost-aware per-user/session quotas, budgets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Risk owner&lt;/td&gt;
&lt;td&gt;SRE/Platform&lt;/td&gt;
&lt;td&gt;Architecture/Product/Finance jointly&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The key insight: &lt;strong&gt;DoS protection is necessary but insufficient&lt;/strong&gt;. You need both performance-based and cost-based controls. DoW can occur even when all your DoS defenses pass. A single user running expensive workflows can dominate your monthly spend without ever triggering a 429 Too Many Requests response.&lt;/p&gt;
&lt;h3 id=&quot;real-world-dow-scenarios&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#real-world-dow-scenarios&quot; aria-label=&quot;real world dow scenarios permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Real-World DoW Scenarios&lt;/h3&gt;
&lt;p&gt;To make this concrete, let&apos;s examine two scenarios that illustrate how DoW manifests in production systems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scenario 1: The Curious Power User&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Sarah, a legitimate user on your free tier, discovers your AI-powered document analysis feature. She uploads PDFs that trigger GPT-4o summarization. Here&apos;s how it unfolds:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Day 1&lt;/strong&gt;: Sarah makes 50 requests/hour (well within your 100 req/hour limit)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each request processes 20,000 tokens = $0.25 per request&lt;/li&gt;
&lt;li&gt;Hourly cost: $12.50/hour&lt;/li&gt;
&lt;li&gt;Daily cost: $300/day&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Your DoS monitoring dashboard&lt;/strong&gt;: All green&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RPS: 0.83 req/sec (far below limits)&lt;/li&gt;
&lt;li&gt;Latency: p95 = 2.1s (acceptable)&lt;/li&gt;
&lt;li&gt;Error rate: 0%&lt;/li&gt;
&lt;li&gt;CPU/memory: Normal&lt;/li&gt;
&lt;li&gt;No alerts fire&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Your DoW reality&lt;/strong&gt;: One user costs $300/day = $9,000/month&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Free tier budget allocation: $10/user/month&lt;/li&gt;
&lt;li&gt;Actual loss: $8,990/month on this single user&lt;/li&gt;
&lt;li&gt;If 100 users discover this pattern: $900,000/month burn&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Scenario 2: The Malicious Actor&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;An attacker reverse-engineers your &lt;code class=&quot;language-text&quot;&gt;/api/search&lt;/code&gt; endpoint and discovers two code paths:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cache hit: Instant response, negligible cost&lt;/li&gt;
&lt;li&gt;LLM semantic search fallback: 800ms latency, $0.30 cost&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The attack pattern:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Craft queries designed to miss cache (unique nonsensical phrases, random strings)&lt;/li&gt;
&lt;li&gt;Stay at 90% of your rate limit (90 req/min from your 100 req/min limit)&lt;/li&gt;
&lt;li&gt;Force expensive LLM fallback on every single request&lt;/li&gt;
&lt;li&gt;Run continuously from multiple API keys to stay under per-key limits&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Cost impact breakdown:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Per API key: 90 requests/min × $0.30 = $27/minute = $1,620/hour&lt;/li&gt;
&lt;li&gt;Daily cost per key: $38,880/day&lt;/li&gt;
&lt;li&gt;With just 10 API keys: $388,800/day&lt;/li&gt;
&lt;li&gt;Traditional rate limiter verdict: &quot;Everything normal, users at 90% of allowed RPS&quot;&lt;/li&gt;
&lt;li&gt;Your monthly bill: Catastrophic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;How cost-aware rate limiting would prevent both scenarios:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In both cases, a cost-aware rate limiter would have:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Pre-execution check&lt;/strong&gt;: Before invoking the costly LLM, estimate the request cost based on workflow type or any other heuristics&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Budget check&lt;/strong&gt;: Query the user&apos;s budget (more on this in the &lt;a href=&quot;https://handsonarchitects.com/blog/2025/denial-of-wallet-cost-aware-rate-limiting-part-2/&quot;&gt;next part&lt;/a&gt;) to see how much spend remains&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enforcement decision&lt;/strong&gt;: Decide to allow or reject based on remaining budget, not just RPS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Graceful rejection&lt;/strong&gt;: Return &lt;code class=&quot;language-text&quot;&gt;429 Too Many Requests&lt;/code&gt; with headers:
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;X-RateLimit-Cost-Remaining: 0
X-RateLimit-Cost-Reset: 1637012400
Retry-After: 3600&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost attribution&lt;/strong&gt;: Record actual cost post-execution for observability and billing&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Sarah would have received clear feedback after hitting her $10/month limit, prompting an upgrade conversation. The attacker would have been throttled after exhausting their allocated budget, regardless of staying under RPS limits.&lt;/p&gt;
&lt;h2 id=&quot;anatomy-of-enterprise-rate-limiting&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#anatomy-of-enterprise-rate-limiting&quot; aria-label=&quot;anatomy of enterprise rate limiting permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Anatomy of Enterprise Rate Limiting&lt;/h2&gt;
&lt;p&gt;Understanding where cost-aware limits fit requires mapping the typical layers of enterprise traffic management. Each layer sees different information and enforces different policies. Cost context becomes visible only at specific points in the stack.&lt;/p&gt;
&lt;h3 id=&quot;cdn-and-waf-strengths-and-blind-spots&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cdn-and-waf-strengths-and-blind-spots&quot; aria-label=&quot;cdn and waf strengths and blind spots permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CDN and WAF Strengths and Blind Spots&lt;/h3&gt;
&lt;p&gt;Your CDN and Web Application Firewall sit at the edge. They see raw HTTP requests from IP addresses. They&apos;re excellent at detecting volumetric attacks, geographic anomalies, and malicious patterns. They enforce coarse-grained limits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Requests per second per IP or region&lt;/li&gt;
&lt;li&gt;Request size limits&lt;/li&gt;
&lt;li&gt;Path-based throttling (e.g., 10 req/s for &lt;code class=&quot;language-text&quot;&gt;/api/*&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What they don&apos;t see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;User identity&lt;/strong&gt;: Which authenticated user is making the request&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Session or user/tenant context&lt;/strong&gt;: Whether this is a free trial or enterprise account&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost implications&lt;/strong&gt;: Which backend services will be invoked&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Business logic&lt;/strong&gt;: Whether the request will hit cache or trigger expensive operations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;CDN and WAF limits protect your infrastructure from brute-force abuse. They&apos;re your first line of defense against traditional DoS. But they operate on metadata-headers, paths, source IPs-not on the semantic meaning or cost of each request.&lt;/p&gt;
&lt;p&gt;Most CDNs and WAFs cannot natively enforce cost-aware limits. In practice, most teams implement cost-aware limiting at the application layer (or integrate it into their service mesh) where cost context is naturally available, and use CDN/WAF for traditional DoS protection.&lt;/p&gt;
&lt;h3 id=&quot;application-layer-tying-requests-to-users-sessions-tenants-plans&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#application-layer-tying-requests-to-users-sessions-tenants-plans&quot; aria-label=&quot;application layer tying requests to users sessions tenants plans permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Application Layer: Tying Requests to Users, Sessions, Tenants, Plans&lt;/h3&gt;
&lt;p&gt;Cost-aware rate limiting happens in your application layer. This is where you have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Authentication context&lt;/strong&gt;: User ID, API key, OAuth token&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authorization context&lt;/strong&gt;: Product plan, subscription tier, feature flags&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Business logic visibility&lt;/strong&gt;: Which code paths will execute, which external services will be called&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost attribution&lt;/strong&gt;: The ability to estimate or measure per-request costs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this layer, you can answer questions like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;How much has this user spent in the last hour?&quot;&lt;/li&gt;
&lt;li&gt;&quot;Is this tenant approaching their monthly budget?&quot;&lt;/li&gt;
&lt;li&gt;&quot;Should we serve from cache or allow an LLM call?&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is where you implement custom rate limiting (e.g. using a distributed cache like Redis or in-memory state stores). You track cost-weighted tokens per user, per session, or per tenant. You enforce per-plan quotas. You integrate with observability to detect spend anomalies.&lt;/p&gt;
&lt;p&gt;&lt;svg id=&quot;mermaid-0&quot; width=&quot;100%&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; class=&quot;flowchart&quot; style=&quot;max-width: 1353.234375px;&quot; viewBox=&quot;0 0 1353.234375 203.4112091064453&quot; role=&quot;graphics-document document&quot; aria-roledescription=&quot;flowchart-v2&quot;&gt;&lt;style&gt;#mermaid-0{font-family:arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-0 .error-icon{fill:#552222;}#mermaid-0 .error-text{fill:#552222;stroke:#552222;}#mermaid-0 .edge-thickness-normal{stroke-width:1px;}#mermaid-0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-0 .marker{fill:#333333;stroke:#333333;}#mermaid-0 .marker.cross{stroke:#333333;}#mermaid-0 svg{font-family:arial,sans-serif;font-size:16px;}#mermaid-0 p{margin:0;}#mermaid-0 .label{font-family:arial,sans-serif;color:#333;}#mermaid-0 .cluster-label text{fill:#333;}#mermaid-0 .cluster-label span{color:#333;}#mermaid-0 .cluster-label span p{background-color:transparent;}#mermaid-0 .label text,#mermaid-0 span{fill:#333;color:#333;}#mermaid-0 .node rect,#mermaid-0 .node circle,#mermaid-0 .node ellipse,#mermaid-0 .node polygon,#mermaid-0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-0 .rough-node .label text,#mermaid-0 .node .label text,#mermaid-0 .image-shape .label,#mermaid-0 .icon-shape .label{text-anchor:middle;}#mermaid-0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-0 .rough-node .label,#mermaid-0 .node .label,#mermaid-0 .image-shape .label,#mermaid-0 .icon-shape .label{text-align:center;}#mermaid-0 .node.clickable{cursor:pointer;}#mermaid-0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-0 .arrowheadPath{fill:#333333;}#mermaid-0 .edgePath .path{stroke:#333333;stroke-width:1px;}#mermaid-0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-0 .cluster text{fill:#333;}#mermaid-0 .cluster span{color:#333;}#mermaid-0 .node .collapsed-indicator{fill:#aaaa33;stroke:none;opacity:0.6;}#mermaid-0 .node .collapsed-separator{stroke:#aaaa33;stroke-width:0.75px;}#mermaid-0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-0 rect.text{fill:none;stroke-width:0;}#mermaid-0 .icon-shape,#mermaid-0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-0 .icon-shape p,#mermaid-0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-0 .icon-shape .label rect,#mermaid-0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-0 .node .neo-node{stroke:#9370DB;}#mermaid-0 [data-look=&quot;neo&quot;].node rect,#mermaid-0 [data-look=&quot;neo&quot;].cluster rect,#mermaid-0 [data-look=&quot;neo&quot;].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].swimlane.cluster rect{filter:none;}#mermaid-0 [data-look=&quot;neo&quot;].node path{stroke:#9370DB;stroke-width:1px;}#mermaid-0 [data-look=&quot;neo&quot;].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].node .neo-line path{stroke:#9370DB;filter:none;}#mermaid-0 [data-look=&quot;neo&quot;].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].node circle .state-start{fill:#000000;}#mermaid-0 [data-look=&quot;neo&quot;].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 [data-look=&quot;neo&quot;].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#mermaid-0 :root{--mermaid-font-family:arial,sans-serif;}#mermaid-0 .cost&gt;*{stroke:rgb(230, 0, 0)!important;stroke-width:2px!important;}#mermaid-0 .cost span{stroke:rgb(230, 0, 0)!important;stroke-width:2px!important;}&lt;/style&gt;&lt;g&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-pointEnd&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 10 5 L 0 10 z&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-pointStart&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;4.5&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;8&quot; markerHeight=&quot;8&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 5 L 10 10 L 10 0 z&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-pointEnd-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 11.5 14&quot; refX=&quot;11.5&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;10.5&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 0 0 L 11.5 7 L 0 14 z&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-pointStart-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 11.5 14&quot; refX=&quot;1&quot; refY=&quot;7&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11.5&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;polygon points=&quot;0,7 11.5,14 11.5,0&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/polygon&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-circleEnd&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;11&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-circleStart&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;-1&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 1; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-circleEnd-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refY=&quot;5&quot; refX=&quot;12.25&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;14&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-circleStart-margin&quot; class=&quot;marker flowchart-v2&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;-2&quot; refY=&quot;5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;14&quot; markerHeight=&quot;14&quot; orient=&quot;auto&quot;&gt;&lt;circle cx=&quot;5&quot; cy=&quot;5&quot; r=&quot;5&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 0; stroke-dasharray: 1, 0;&quot;&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-crossEnd&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 11 11&quot; refX=&quot;12&quot; refY=&quot;5.2&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 l 9,9 M 10,1 l -9,9&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-crossStart&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 11 11&quot; refX=&quot;-1&quot; refY=&quot;5.2&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;11&quot; markerHeight=&quot;11&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 l 9,9 M 10,1 l -9,9&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-crossEnd-margin&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 15 15&quot; refX=&quot;17.7&quot; refY=&quot;7.5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 L 14,14 M 1,14 L 14,1&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2.5;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id=&quot;mermaid-0_flowchart-v2-crossStart-margin&quot; class=&quot;marker cross flowchart-v2&quot; viewBox=&quot;0 0 15 15&quot; refX=&quot;-3.5&quot; refY=&quot;7.5&quot; markerUnits=&quot;userSpaceOnUse&quot; markerWidth=&quot;12&quot; markerHeight=&quot;12&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M 1,1 L 14,14 M 1,14 L 14,1&quot; class=&quot;arrowMarkerPath&quot; style=&quot;stroke-width: 2.5; stroke-dasharray: 1, 0;&quot;&gt;&lt;/path&gt;&lt;/marker&gt;&lt;g class=&quot;root&quot;&gt;&lt;g class=&quot;clusters&quot;&gt;&lt;/g&gt;&lt;g class=&quot;edgePaths&quot;&gt;&lt;path d=&quot;M147.141,101.074L151.307,101.074C155.474,101.074,163.807,101.074,171.474,101.074C179.141,101.074,186.141,101.074,189.641,101.074L193.141,101.074&quot; id=&quot;mermaid-0-L_U_C_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_U_C_0&quot; data-points=&quot;W3sieCI6MTQ3LjE0MDYyNSwieSI6MTAxLjA3MzUyODI4OTc5NDkyfSx7IngiOjE3Mi4xNDA2MjUsInkiOjEwMS4wNzM1MjgyODk3OTQ5Mn0seyJ4IjoxOTcuMTQwNjI1LCJ5IjoxMDEuMDczNTI4Mjg5Nzk0OTJ9XQ==&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M384.313,101.074L388.479,101.074C392.646,101.074,400.979,101.074,408.646,101.074C416.313,101.074,423.313,101.074,426.813,101.074L430.313,101.074&quot; id=&quot;mermaid-0-L_C_W_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_C_W_0&quot; data-points=&quot;W3sieCI6Mzg0LjMxMjUsInkiOjEwMS4wNzM1MjgyODk3OTQ5Mn0seyJ4Ijo0MDkuMzEyNSwieSI6MTAxLjA3MzUyODI4OTc5NDkyfSx7IngiOjQzNC4zMTI1LCJ5IjoxMDEuMDczNTI4Mjg5Nzk0OTJ9XQ==&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M635.984,101.074L640.151,101.074C644.318,101.074,652.651,101.074,660.318,101.074C667.984,101.074,674.984,101.074,678.484,101.074L681.984,101.074&quot; id=&quot;mermaid-0-L_W_L_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_W_L_0&quot; data-points=&quot;W3sieCI6NjM1Ljk4NDM3NSwieSI6MTAxLjA3MzUyODI4OTc5NDkyfSx7IngiOjY2MC45ODQzNzUsInkiOjEwMS4wNzM1MjgyODk3OTQ5Mn0seyJ4Ijo2ODUuOTg0Mzc1LCJ5IjoxMDEuMDczNTI4Mjg5Nzk0OTJ9XQ==&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M849.172,101.074L853.339,101.074C857.505,101.074,865.839,101.074,873.505,101.074C881.172,101.074,888.172,101.074,891.672,101.074L895.172,101.074&quot; id=&quot;mermaid-0-L_L_A_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_L_A_0&quot; data-points=&quot;W3sieCI6ODQ5LjE3MTg3NSwieSI6MTAxLjA3MzUyODI4OTc5NDkyfSx7IngiOjg3NC4xNzE4NzUsInkiOjEwMS4wNzM1MjgyODk3OTQ5Mn0seyJ4Ijo4OTkuMTcxODc1LCJ5IjoxMDEuMDczNTI4Mjg5Nzk0OTJ9XQ==&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1082.671,74.074L1098.94,68.681C1115.208,63.289,1147.745,52.505,1177.148,47.113C1206.552,41.721,1232.823,41.721,1245.958,41.721L1259.094,41.721&quot; id=&quot;mermaid-0-L_A_X_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_A_X_0&quot; data-points=&quot;W3sieCI6MTA4Mi42NzEyNjAzODM2MTMxLCJ5Ijo3NC4wNzM1MjgyODk3OTQ5Mn0seyJ4IjoxMTgwLjI4MTI1LCJ5Ijo0MS43MjA3MjYwMTMxODM1OTR9LHsieCI6MTI2My4wOTM3NSwieSI6NDEuNzIwNzI2MDEzMTgzNTk0fV0=&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1082.671,128.074L1098.94,133.466C1115.208,138.858,1147.745,149.642,1176.185,155.034C1204.625,160.426,1228.969,160.426,1241.141,160.426L1253.313,160.426&quot; id=&quot;mermaid-0-L_A_D_0&quot; class=&quot;edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link&quot; style=&quot;;&quot; data-edge=&quot;true&quot; data-et=&quot;edge&quot; data-id=&quot;L_A_D_0&quot; data-points=&quot;W3sieCI6MTA4Mi42NzEyNjAzODM2MTMxLCJ5IjoxMjguMDczNTI4Mjg5Nzk0OTJ9LHsieCI6MTE4MC4yODEyNSwieSI6MTYwLjQyNjMzMDU2NjQwNjI1fSx7IngiOjEyNTcuMzEyNSwieSI6MTYwLjQyNjMzMDU2NjQwNjI1fV0=&quot; data-look=&quot;classic&quot; marker-end=&quot;url(#mermaid-0_flowchart-v2-pointEnd)&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabels&quot;&gt;&lt;g class=&quot;edgeLabel&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_U_C_0&quot; transform=&quot;translate(0, 0)&quot;&gt;&lt;foreignObject width=&quot;0&quot; height=&quot;0&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabel&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_C_W_0&quot; transform=&quot;translate(0, 0)&quot;&gt;&lt;foreignObject width=&quot;0&quot; height=&quot;0&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabel&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_W_L_0&quot; transform=&quot;translate(0, 0)&quot;&gt;&lt;foreignObject width=&quot;0&quot; height=&quot;0&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabel&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_L_A_0&quot; transform=&quot;translate(0, 0)&quot;&gt;&lt;foreignObject width=&quot;0&quot; height=&quot;0&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabel&quot; transform=&quot;translate(1180.28125, 41.720726013183594)&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_A_X_0&quot; transform=&quot;translate(-52.03125, -12)&quot;&gt;&lt;foreignObject width=&quot;104.0625&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;p&gt;Expensive ops&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;edgeLabel&quot; transform=&quot;translate(1180.28125, 160.42633056640625)&quot;&gt;&lt;g class=&quot;label&quot; data-id=&quot;L_A_D_0&quot; transform=&quot;translate(-38.6953125, -12)&quot;&gt;&lt;foreignObject width=&quot;77.390625&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; class=&quot;labelBkg&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;edgeLabel&quot;&gt;&lt;p&gt;Cheap ops&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;nodes&quot;&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-U-0&quot; data-look=&quot;classic&quot; transform=&quot;translate(77.5703125, 101.07352828979492)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;&quot; x=&quot;-69.5703125&quot; y=&quot;-27&quot; width=&quot;139.140625&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-39.5703125, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;79.140625&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;User/Client&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-C-1&quot; data-look=&quot;classic&quot; transform=&quot;translate(290.7265625, 101.07352828979492)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;&quot; x=&quot;-93.5859375&quot; y=&quot;-27&quot; width=&quot;187.171875&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-63.5859375, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;127.171875&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;CDN/Edge Cache&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-W-3&quot; data-look=&quot;classic&quot; transform=&quot;translate(535.1484375, 101.07352828979492)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;&quot; x=&quot;-100.8359375&quot; y=&quot;-27&quot; width=&quot;201.671875&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-70.8359375, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;141.671875&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;API Gateway / WAF&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-L-5&quot; data-look=&quot;classic&quot; transform=&quot;translate(767.578125, 101.07352828979492)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;&quot; x=&quot;-81.59375&quot; y=&quot;-27&quot; width=&quot;163.1875&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-51.59375, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;103.1875&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Load Balancer&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-A-7&quot; data-look=&quot;classic&quot; transform=&quot;translate(1001.2109375, 101.07352828979492)&quot;&gt;&lt;rect class=&quot;basic label-container&quot; style=&quot;&quot; x=&quot;-102.0390625&quot; y=&quot;-27&quot; width=&quot;204.078125&quot; height=&quot;54&quot;&gt;&lt;/rect&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-72.0390625, -12)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;144.078125&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;Application Services&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default cost&quot; id=&quot;mermaid-0-flowchart-X-9&quot; data-look=&quot;classic&quot; transform=&quot;translate(1301.2734375, 41.720726013183594)&quot;&gt;&lt;path d=&quot;M0,9.480484208892682 a38.1796875,9.480484208892682 0,0,0 76.359375,0 a38.1796875,9.480484208892682 0,0,0 -76.359375,0 l0,48.48048420889268 a38.1796875,9.480484208892682 0,0,0 76.359375,0 l0,-48.48048420889268&quot; class=&quot;basic label-container outer-path&quot; style=&quot;stroke:#e60000 !important;stroke-width:2px !important&quot; transform=&quot;translate(-38.1796875, -33.72072631333902)&quot;&gt;&lt;/path&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-30.6796875, -2)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;61.359375&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;LLM/API&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class=&quot;node default&quot; id=&quot;mermaid-0-flowchart-D-11&quot; data-look=&quot;classic&quot; transform=&quot;translate(1301.2734375, 160.42633056640625)&quot;&gt;&lt;path d=&quot;M0,10.32325530197402 a43.9609375,10.32325530197402 0,0,0 87.921875,0 a43.9609375,10.32325530197402 0,0,0 -87.921875,0 l0,49.32325530197402 a43.9609375,10.32325530197402 0,0,0 87.921875,0 l0,-49.32325530197402&quot; class=&quot;basic label-container outer-path&quot; style=&quot;&quot; transform=&quot;translate(-43.9609375, -34.98488295296103)&quot;&gt;&lt;/path&gt;&lt;g class=&quot;label&quot; style=&quot;&quot; transform=&quot;translate(-36.4609375, -2)&quot;&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width=&quot;72.921875&quot; height=&quot;24&quot;&gt;&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot; style=&quot;display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;&quot;&gt;&lt;span class=&quot;nodeLabel&quot;&gt;&lt;p&gt;DB/Cache&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;defs&gt;&lt;filter id=&quot;mermaid-0-drop-shadow&quot; height=&quot;130%&quot; width=&quot;130%&quot;&gt;&lt;feDropShadow dx=&quot;4&quot; dy=&quot;4&quot; stdDeviation=&quot;0&quot; flood-opacity=&quot;0.06&quot; flood-color=&quot;#000000&quot;&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;defs&gt;&lt;filter id=&quot;mermaid-0-drop-shadow-small&quot; height=&quot;150%&quot; width=&quot;150%&quot;&gt;&lt;feDropShadow dx=&quot;2&quot; dy=&quot;2&quot; stdDeviation=&quot;0&quot; flood-opacity=&quot;0.06&quot; flood-color=&quot;#000000&quot;&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;/svg&gt;&lt;/p&gt;
&lt;h3 id=&quot;observability-metrics-you-need-before-building-cost-aware-limits&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#observability-metrics-you-need-before-building-cost-aware-limits&quot; aria-label=&quot;observability metrics you need before building cost aware limits permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Observability: Metrics You Need Before Building Cost-Aware Limits&lt;/h3&gt;
&lt;p&gt;Before implementing custom rate limiting, instrument your application to collect baseline cost data:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Per-request cost metrics&lt;/strong&gt;: Tag each request with estimated or actual cost. Export to Prometheus, Datadog, or CloudWatch.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Per-user/tenant aggregations&lt;/strong&gt;: Sum costs over rolling time windows (1m, 5m, 1h, 1d).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost distribution&lt;/strong&gt;: Track p50, p95, p99 costs per endpoint or workflow to understand variance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request mix tracking&lt;/strong&gt;: Log which code paths executed (cache hit vs LLM call) to identify high-cost patterns.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agentic workflow tracing&lt;/strong&gt;: For complex multi-step operations, trace the full call graph to attribute costs accurately (some useful tooling exists already for this, e.g. LangFuse).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Without these metrics, you&apos;re flying blind. You won&apos;t know which users are driving costs, which operations are expensive, or where to set budget thresholds. Start with observability, then build controls.&lt;/p&gt;
&lt;p&gt;Having said that, if you are working on a greenfield project with LLM integration and you don&apos;t yet have cost data, it&apos;s reasonable to start with educated estimates based on how much your business is willing to spend per user/session/tenant. You can refine your limits as real data comes in.&lt;/p&gt;
&lt;h2 id=&quot;cost-awareness-in-system-design&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cost-awareness-in-system-design&quot; aria-label=&quot;cost awareness in system design permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Cost Awareness in System Design&lt;/h2&gt;
&lt;p&gt;Designing cost-aware systems starts with modeling heterogeneous request costs. Not all operations are created equal. Even within a single endpoint, different execution paths can have wildly different price tags.&lt;/p&gt;
&lt;h3 id=&quot;modeling-costs-per-workflow-vs-per-endpoint&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#modeling-costs-per-workflow-vs-per-endpoint&quot; aria-label=&quot;modeling costs per workflow vs per endpoint permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Modeling Costs per Workflow vs per Endpoint&lt;/h3&gt;
&lt;p&gt;Traditional rate limiting thinks in terms of endpoints: &lt;code class=&quot;language-text&quot;&gt;/api/search&lt;/code&gt; gets 100 req/min, &lt;code class=&quot;language-text&quot;&gt;/api/upload&lt;/code&gt; gets 10 req/min. Cost-aware rate limiting thinks in terms of workflows and resource consumption. The currency here isn’t time: LLM cost is driven by tokens (requests × tokens in/out), not latency.&lt;/p&gt;
&lt;p&gt;Consider a search endpoint that can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Serve from an in-memory cache (cost: $0.0001)&lt;/li&gt;
&lt;li&gt;Query Elasticsearch (cost: $0.001)&lt;/li&gt;
&lt;li&gt;Fall back to an LLM for semantic understanding (cost: $0.20)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The endpoint is the same. The workflow cost varies by 2,000x. A user could make 100 requests per minute, all hitting the LLM fallback, and stay within your RPS limit while burning through $20/minute or $1,200/hour.&lt;/p&gt;
&lt;p&gt;To model this, define &lt;strong&gt;cost units&lt;/strong&gt; for each workflow:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Map operation -&gt; cost units (normalized to $ or credits)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;COST_TABLE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&apos;search.basic&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0001&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&apos;search.es&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.001&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&apos;search.llm&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.2&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;estimateCost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;operation&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; params&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Optionally scale by tokens, size, or retries&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; base &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;COST_TABLE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;operation&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; multiplier &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;tokens &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tokens &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; base &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; multiplier&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This simple table lets you attach a cost estimate to every request. You can refine it over time based on real billing data. The key is making cost a first-class metric alongside latency and error rate. We will cover this in more detail in following parts of this series.&lt;/p&gt;
&lt;h3 id=&quot;measuring-and-attributing-cost-to-userssessionstenants&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#measuring-and-attributing-cost-to-userssessionstenants&quot; aria-label=&quot;measuring and attributing cost to userssessionstenants permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Measuring and Attributing Cost to Users/Sessions/Tenants&lt;/h3&gt;
&lt;p&gt;Cost-aware rate limiting requires attributing costs to specific actors. You need counters keyed by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;User ID&lt;/strong&gt;: Track individual user spend&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Session ID&lt;/strong&gt;: Limit costs per browsing session (useful for anonymous users)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tenant ID&lt;/strong&gt;: Enforce per-organization budgets in B2B SaaS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Product plan&lt;/strong&gt;: Apply different budgets for free, pro, and enterprise tiers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ensure these limits are clearly disclosed to customers in SLA and other customer-facing documents.&lt;/p&gt;
&lt;p&gt;Example cost attribution flow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Request arrives with authentication token&lt;/li&gt;
&lt;li&gt;Extract user ID and product plan&lt;/li&gt;
&lt;li&gt;Estimate request cost from workflow type&lt;/li&gt;
&lt;li&gt;Check if user&apos;s current spend + estimated cost &amp;#x3C;= budget&lt;/li&gt;
&lt;li&gt;Allow or deny request&lt;/li&gt;
&lt;li&gt;Record actual cost post-execution for observability&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This requires fast lookups. Redis is a common choice for storing per-user cost counters with TTLs matching your time windows (e.g., 1-hour sliding windows). We will cover implementation details in part 2 of this series.&lt;/p&gt;
&lt;h2 id=&quot;when-to-invest-in-custom-rate-limiting&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#when-to-invest-in-custom-rate-limiting&quot; aria-label=&quot;when to invest in custom rate limiting permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;When to Invest in Custom Rate Limiting&lt;/h2&gt;
&lt;p&gt;Not every system needs cost-aware rate limiting. Building and maintaining custom logic adds complexity. You need to balance the cost of implementation against the risk of budget exposure. Here are clear indicators for when to invest.&lt;/p&gt;
&lt;h3 id=&quot;decision-checklist-must-have-signals&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#decision-checklist-must-have-signals&quot; aria-label=&quot;decision checklist must have signals permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Decision Checklist (Must-Have Signals)&lt;/h3&gt;
&lt;p&gt;Invest in custom cost-aware rate limiting if you see these patterns:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Material cost variance (≥10–100x) across request types&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Quantified threshold: p99 cost / p50 cost &gt; 100x&lt;/li&gt;
&lt;li&gt;Example: LLM calls ($0.30) vs cache hits ($0.0001) = 3,000x variance&lt;/li&gt;
&lt;li&gt;If your cheapest and most expensive operations differ by 100x or more, uniform rate limits can&apos;t protect you.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Unpredictable or spiky workloads&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Quantified threshold: Top 5% of users drive &gt;50% of total spend, or week-over-week cost variance &gt;30%&lt;/li&gt;
&lt;li&gt;Example: Power users discovering expensive workflows or seasonal usage spikes (look at current - Q4 2025 - Antropic Claude Code weekly limits ;) )&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Clear budget constraints (per week/month or per tenant/plan)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Quantified threshold: Hard monthly budget cap exists, or per-tenant contractual limits are in place&lt;/li&gt;
&lt;li&gt;Example: Free tier limited to $10/month per user&lt;/li&gt;
&lt;li&gt;Finance cares about the bill, not the RPS.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;wrapping-up-part-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#wrapping-up-part-1&quot; aria-label=&quot;wrapping up part 1 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Wrapping Up Part 1&lt;/h2&gt;
&lt;p&gt;This post introduces &lt;em&gt;Denial of Wallet (DoW)&lt;/em&gt; as a failure mode distinct from traditional &lt;em&gt;Denial of Service (DoS)&lt;/em&gt;. While DoS attacks overwhelm system performance, DoW exploits cost differences in modern applications (especially those involving LLMs, vector search, and image processing) where some requests are orders of magnitude times more expensive than others. Uniform rate limits based on requests-per-second no longer protect budgets, because even low-volume users can trigger costly operations without breaching performance limits.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;agentic-conversation-note&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#agentic-conversation-note&quot; aria-label=&quot;agentic conversation note permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Agentic conversation note&lt;/h3&gt;
&lt;p&gt;For simplicity, assume that agentic conversation is a workflow where we have a &quot;coordinator&quot; LLM that supervises the API request execution, calls other LLMs/tools as needed, and aggregates results. This is more expensive than a single LLM call due to multiple invocations and orchestration overhead.&lt;/p&gt;
&lt;h3 id=&quot;llm-tokens-note&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#llm-tokens-note&quot; aria-label=&quot;llm tokens note permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LLM tokens note&lt;/h3&gt;
&lt;p&gt;For the sake of this post, think of tokens as unit of data that LLMs process. E.g. imagine API that processes text and provides a summary. Then &quot;input tokens&quot; is the size (e.g. number of words/characters) of the text you send to API, and &quot;output tokens&quot; is the size of the summary you get back. LLM pricing is often based on number of input and output tokens processed. In practice, it is hard to estimate exact token counts before making the API call (especially output tokens - but some LLM APIs let you set a maximum).&lt;/p&gt;</content:encoded></item><item><title><![CDATA[The Staff Engineer Toolkit]]></title><description><![CDATA[The Staff Engineer Toolkit A collection of tools, techniques, and reflections from our experience working as Staff Engineers in large-scale…]]></description><link>https://handsonarchitects.com/blog/2025/staff-engineer-toolkit/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2025/staff-engineer-toolkit/</guid><pubDate>Thu, 06 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 512px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/65d76f2affb2bea1fc266bc2004d1168/01e7c/staff-engineer-toolkit.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEz0lEQVR42gHEBDv7APzXmPzanPzcoPzcof7kqf7kqe7OluzMlPrdov7hpvbWnOTCiOnFiunFi+bEi+TCieTDiuPCiuTDiebFjAD82Jn82Zv72p3/46jix5KQbkdgOBlmPBt9Wzft1Z/mwIaEShp2Qhp8RhyCSh1+SR2BSR2DRxmGSh2RVCMA+9ia+tea/9+i5caPckglh1MmwoJFpGo1jFYnspZq6sOJcEUcZls4XkMkekEZn0oWdzgXWUg2VT8sgj8YAPzZm/nWmP/kpr2dbV8wEMuWVuOnXvK0ZtWaVLyfb+rDiHBJHn6AVJprOXNVNqxQFoBOLUdeW2RJLYBAFgD82Zv41JX/36HBn2xsRSC2hUysfke3hUuZbTuymGznwIdwRx16d02ZaTd3WDqZShR7TS5GVlBvSCiHQhcA+9eY99GS/tubvJBZxohG2qBbxY1Oy5RTqHdAvp9w58GIcEcceXZLlmc2hWA8slIUeE80RWdoW0o0gD0UAPrWl/TNjPjRj+PBh8uaXf25aNCYVdyfWcOHR9m6g+vDiXBIHXt8UYllNohVJ6FUHIhRJXJdPnVRLIlNHQD41JXxx4b30Y/atHuxdT3cm1Xlp2DYm1bTn17u05vnvoJ6TyF3bEFhVjZoQCJxRidtRyhpRSlnRSeXVSIA+9WV8s2MypBVvV8bx2Udy4M/z45Mu3k8tWww7MyT5L6Ci0wbk1clZ1I7UkQ0U0Y2Vkg6UkY5W0cyvXItAPbXmcGRW89fFMlkHMZiG7VeH7p5PKJSGsxZDsuHSt6+hZ1WHqNkLllJOFZFNVdHNVdFM1NGN2xPMtKAMQCZk3SIUCX7eB3TaR7KZB3gahuyVxnUaR7ZaRyzXiHUpWjUfjClbDZQRDZURDJPPy1UQzBPQTJ4VC/ZhDMAZ2xdkVUn4mkXw14az2Qb8XUfz2cd5m8c02Ubn1IbsXc9v3YwiFwzVUc5U0IwTTwpV0c1Qzswi1wv3IYzAGlrW3lRLc59NqhqMsBhH+RuHeBpGtdxJrRjJceGRdebVbaCRm9UN1VGNlNCME89K1tHM4RWKqxqLMZ5MAB5eWRNSTnpn1SqdD6ZZDPBhETYl1DUnFi6ik6TaTl/WzN5WDRVRDFIOCabYSpqSitUQzKpaCzLeS/JezEAmo5tgUMevlccpkoXnkcXsF0mz3Y1mV4te1o0T0AtPTMnRjcneU8pfU8kVj8oSjooTTomcksmvXIsrmgpAM2vfaNSILZWHbxWHLhVHK1PGq1IFZZEFpJZJYZVKZVcKaNiJrRrKLxxK6VlKpFaJ5hZI6FcIoZKHYBFHADCgDy4VxyzUhqWSBmcSxq5WB+zUBqnTBuYViGpYiW6birJeS+9ci2fXiSRVSGRUh+CRhyFRxyXUSGbVyMAx3YstlUbmEcXmEgZkkcZrlEav1Qbq0wagUYbf0QbfkQbiEsejFAgiEkde0EbgUQcnVYirGEmrWInn1skAMl6MLJhI6VUHqFOG5NFGLVSGrpQGa1WHsF0LaJdJIJHHXM7GY5QH5dRIaRaJJ9aI7dnKLFkJ7BjJ6NcJQDSgTPPfzLNfDHAdC29bSm4ZSWeWSKhXyWUVSGCSR2HSR54PhqqZCixZSe2ZiiiXCSfWyS2ZyizZiiqYSX+Fhdjg4qUuwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Staff Engineer Toolkit hero image&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/65d76f2affb2bea1fc266bc2004d1168/01e7c/staff-engineer-toolkit.png&quot;
        srcset=&quot;/static/65d76f2affb2bea1fc266bc2004d1168/ff46a/staff-engineer-toolkit.png 325w,
/static/65d76f2affb2bea1fc266bc2004d1168/01e7c/staff-engineer-toolkit.png 512w&quot;
        sizes=&quot;(max-width: 512px) 100vw, 512px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-staff-engineer-toolkit&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-staff-engineer-toolkit&quot; aria-label=&quot;the staff engineer toolkit permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Staff Engineer Toolkit&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A collection of tools, techniques, and reflections from our experience working as Staff Engineers in large-scale distributed systems and reliability-focused organizations.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;who-is-a-staff-engineer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#who-is-a-staff-engineer&quot; aria-label=&quot;who is a staff engineer permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Who is a Staff Engineer?&lt;/h2&gt;
&lt;p&gt;A &lt;strong&gt;Staff Engineer&lt;/strong&gt; is a senior individual contributor who plays a strategic technical leadership role within an organization. It’s often seen as a step up from Senior Engineer - it’s a shift from &quot;doing more&quot; to &quot;thinking broader&quot;. And do not confuse it with management - Staff Engineers remain &lt;strong&gt;individual contributors&lt;/strong&gt;, but they influence at a higher level.&lt;/p&gt;
&lt;p&gt;You spend less time answering &quot;how&quot; and more time answering &quot;why&quot;. You shape the technical direction of teams, align engineering initiatives with business goals, and guide others to deliver at scale.&lt;/p&gt;
&lt;p&gt;Key characteristics of an effective Staff Engineer include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Strong &lt;strong&gt;technical foundation&lt;/strong&gt; - deep understanding of software design, architecture, and operational excellence.&lt;/li&gt;
&lt;li&gt;Excellent communication skills - you collaborate across teams, departments, and sometimes entire organizations.&lt;/li&gt;
&lt;li&gt;Ability to &lt;strong&gt;influence without authority&lt;/strong&gt; - you lead by trust, not by title.&lt;/li&gt;
&lt;li&gt;Time awareness - your most valuable resource is your &lt;strong&gt;time&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Emotional resilience - ambiguity, imposter syndrome, and unclear expectations are part of your journey.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;staff-engineers-toolkit&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#staff-engineers-toolkit&quot; aria-label=&quot;staff engineers toolkit permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Staff Engineer’s toolkit&lt;/h2&gt;
&lt;p&gt;The following sections summarize practical techniques we’ve found useful to grow and be effective in the Staff Engineer role.&lt;/p&gt;
&lt;h3 id=&quot;define-and-evolve-your-leadership-style&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#define-and-evolve-your-leadership-style&quot; aria-label=&quot;define and evolve your leadership style permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Define and evolve your leadership style&lt;/h3&gt;
&lt;p&gt;Your leadership style is shaped by your personality, values, and experiences. It determines how you influence others and drive change. Different environments reward different styles-some value hierarchical decision-making, others thrive in collaborative, experimental cultures. Be self-aware and find what aligns with your environment.&lt;/p&gt;
&lt;p&gt;We’ve learned that collaborative, empowering leadership works best in the long run. The &lt;strong&gt;organic problem-solving approach&lt;/strong&gt; described by Gerald M. Weinberg in &quot;Becoming a Technical Leader&quot; focuses on enabling and empowering others to succeed - it creates an environment where people feel valued, supported, and motivated to contribute their best work (and reduces the fear of job loss common in more hierarchical organizations). Using a threat/reward or blame culture may yield short-term results, but it often leads to burnout, low morale, and fear-driven behaviors. Moreover, it prevents innovation and risk-taking (which are essential in tech).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lead by influence, not authority - build trust and credibility through technical expertise and effective communication.&lt;/li&gt;
&lt;li&gt;Prioritize team success over individual heroics - avoid &quot;hero culture&quot;, celebrate team wins, and share knowledge to grow together.&lt;/li&gt;
&lt;li&gt;Foster an environment where people feel safe to fail, learn, and share (avoid the blame game and threat/reward culture).&lt;/li&gt;
&lt;li&gt;Apply the &lt;strong&gt;MOI Model (Motivation, Organization, Ideas)&lt;/strong&gt; to lead and influence others:
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Motivation&lt;/em&gt;: understand what drives your team and align it with company goals.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Organization&lt;/em&gt;: navigate organizational dynamics and politics to build alliances and gain support for initiatives.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Ideas&lt;/em&gt;: communicate vision effectively and plant seeds of innovation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Make sure that your leadership style aligns with your organization&apos;s culture and values. Without that alignment, even the best intentions can lead to frustration and ineffectiveness.&lt;/p&gt;
&lt;p&gt;Recommended readings: &lt;a href=&quot;https://www.amazon.com/Becoming-Technical-Leader-Problem-Solving-Approach/dp/0932633021/&quot;&gt;Becoming a Technical Leader: An Organic Problem-Solving Approach&lt;/a&gt; by Gerald M. Weinberg; &lt;a href=&quot;https://www.amazon.com/Accelerate-Software-Performing-Technology-Organizations/dp/1942788339/&quot;&gt;Accelerate: The Science of Lean Software and DevOps: Building and Scaling High Performing Technology Organizations&lt;/a&gt; by Nicole Forsgren, Jez Humble, and Gene Kim&lt;/p&gt;
&lt;h3 id=&quot;develop-an-architectural-mindset&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#develop-an-architectural-mindset&quot; aria-label=&quot;develop an architectural mindset permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Develop an architectural mindset&lt;/h3&gt;
&lt;p&gt;As a Staff Engineer, you are expected to make &lt;strong&gt;architectural decisions&lt;/strong&gt; that balance business goals, technical trade-offs, and team capabilities. Focus on &quot;good enough&quot; solutions rather than perfect ones. Iterate.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Understand your stakeholders&apos; and sponsors&apos; needs and constraints (business drivers, technical limitations, team capabilities).&lt;/li&gt;
&lt;li&gt;Break down complex problems and systems into smaller, manageable components - &lt;strong&gt;build your map&lt;/strong&gt; to navigate complexity with your team, stakeholders, and other teams (e.g., use &lt;a href=&quot;https://c4model.com/&quot;&gt;C4 model&lt;/a&gt; to visualize system architecture, define system context, containers, components, and code).&lt;/li&gt;
&lt;li&gt;Agree on the &quot;-ilities&quot; (non-functional requirements) upfront to guide your architectural decisions (e.g., scalability, maintainability, performance, security) - use the &lt;a href=&quot;https://www.developertoarchitect.com/downloads/worksheets.html&quot;&gt;Architecture Characteristics Worksheet&lt;/a&gt; to document and align on these requirements.&lt;/li&gt;
&lt;li&gt;Aggregate &quot;-ilities&quot; into categories such as operational (e.g., performance, scalability, elasticity), structural (e.g., modularity, maintainability, testability), and cross-cutting (e.g., security, agility, deployability) to simplify decision-making and streamline discussions with stakeholders and engineering teams.&lt;/li&gt;
&lt;li&gt;Document architectural decisions using &lt;a href=&quot;https://adr.github.io/&quot;&gt;ADRs (Architecture Decision Records)&lt;/a&gt; to capture context, options considered, and rationale.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Recommended readings: &lt;a href=&quot;https://www.amazon.com/Fundamentals-Software-Architecture-Engineering-Approach/dp/1098175514/&quot;&gt;Fundamentals of Software Architecture: A Modern Engineering Approach&lt;/a&gt; by Mark Richards and Neal Ford; &lt;a href=&quot;https://www.amazon.com/Software-Architecture-Trade-Off-Distributed-Architectures/dp/1492086894/&quot;&gt;Software Architecture: The Hard Parts: Modern Trade-Off Analyses for Distributed Architectures&lt;/a&gt; by Neal Ford and Mark Richards&lt;/p&gt;
&lt;h3 id=&quot;writing-is-thinking&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#writing-is-thinking&quot; aria-label=&quot;writing is thinking permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Writing is thinking&lt;/h3&gt;
&lt;p&gt;It clarifies ideas, preserves decisions, and enables &lt;strong&gt;asynchronous collaboration&lt;/strong&gt;. In remote and distributed teams, writing becomes even more critical as a communication tool.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Capture everything that matters-designs, roadmaps, retrospectives, meeting notes.&lt;/li&gt;
&lt;li&gt;Use consistent templates (ADR, RFC, design doc) and always include:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; - add executive summaries to your documents to help busy stakeholders quickly grasp key points.&lt;/li&gt;
&lt;li&gt;Audience - define who is expected to read it, who needs to approve it, and who should be informed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context&lt;/strong&gt; - background information and relevant history.&lt;/li&gt;
&lt;li&gt;Problem statement - what issue are you addressing.&lt;/li&gt;
&lt;li&gt;Options considered - alternative solutions evaluated.&lt;/li&gt;
&lt;li&gt;Decision rationale - why you chose this option.&lt;/li&gt;
&lt;li&gt;Consequences - implications, risks, and next steps.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Timebox your writing to avoid perfectionism and overthinking. Iterate based on feedback: check comments and questions from others, then revisit and refine your writing periodically to ensure it remains relevant and accurate.&lt;/li&gt;
&lt;li&gt;Consider using AI tools for summaries, crossing out mind short-cuts. E.g. run your notes through ChatGPT or Claude. Not to reformat, but to make it concise and easier to consume (READ IT AFTERWARDS!). Check our blog on &lt;a href=&quot;https://handsonarchitects.com/blog/2025/ai-toolset-for-software-architect-2025q3/&quot;&gt;using AI for Software Architects&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;build-organizational-awareness&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#build-organizational-awareness&quot; aria-label=&quot;build organizational awareness permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Build organizational awareness&lt;/h3&gt;
&lt;p&gt;In large organizations, technical skill alone is not enough - you must understand how the organization works.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Map key stakeholders, teams, and their relationships to understand the &lt;strong&gt;organizational landscape&lt;/strong&gt; (especially important in larger organizations where you need to navigate multiple teams and departments). You can use tools like org charts, stakeholder mapping matrices, or network diagrams to visualize these relationships.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Identify influencers&lt;/strong&gt; and decision-makers to effectively navigate organizational politics (those can be different from formal authority figures, e.g., senior engineers with strong technical credibility). Understand their motivations, goals, and pain points to build alliances and gain support for your initiatives. Start by reading their documents, adding comments, asking questions, and scheduling 1:1s to build trust over time.&lt;/li&gt;
&lt;li&gt;If possible, &lt;strong&gt;meet people in person&lt;/strong&gt; - it strengthens relationships dramatically.&lt;/li&gt;
&lt;li&gt;Understand the company&apos;s goals, values, and culture to align your technical vision with organizational objectives (check company OKRs, your department goals, your reporting manager&apos;s goals, your team goals).&lt;/li&gt;
&lt;li&gt;Monitor your &lt;strong&gt;reporting chains&lt;/strong&gt; and ensure alignment with your manager and other leaders (e.g., regular documented 1:1s, skip-level meetings, all-hands meetings).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;learn-how-to-learn-continuously&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#learn-how-to-learn-continuously&quot; aria-label=&quot;learn how to learn continuously permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Learn how to learn continuously&lt;/h3&gt;
&lt;p&gt;Continuous learning is a core part of being a Staff Engineer. Think in terms of both &lt;strong&gt;I-shaped skills&lt;/strong&gt; and &lt;strong&gt;T-shaped skills&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;T-shaped skills&lt;/strong&gt; - broad knowledge across multiple areas (architecture, DevOps, cloud, security, etc.).
&lt;ul&gt;
&lt;li&gt;Grow as an &lt;a href=&quot;https://martinfowler.com/articles/expert-generalist.html&quot;&gt;expert generalist&lt;/a&gt; who can see the big picture and connect the dots between different areas.&lt;/li&gt;
&lt;li&gt;Focus on &quot;stuff you don&apos;t know you don&apos;t know&quot; to expand your knowledge and skills beyond your current expertise (e.g., &lt;a href=&quot;https://www.thoughtworks.com/radar&quot;&gt;Thoughtworks Technology Radar&lt;/a&gt; for new trends and technologies - what&apos;s new, what&apos;s coming, what to try, what to hold back on).&lt;/li&gt;
&lt;li&gt;Start your day with a cup of coffee/tea and 15 minutes of reading technical articles and blogs (e.g., &lt;a href=&quot;https://www.infoq.com/&quot;&gt;InfoQ&lt;/a&gt;, &lt;a href=&quot;https://dzone.com/refcardz&quot;&gt;DZone Refcards&lt;/a&gt;, &lt;a href=&quot;https://handsonarchitects.com/&quot;&gt;HandsOnArchitects&lt;/a&gt; 😉).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;I-shaped skills&lt;/strong&gt; - deep expertise in a specific domain (backend, DevOps, reliability, etc.).
&lt;ul&gt;
&lt;li&gt;Become a &lt;strong&gt;go-to person&lt;/strong&gt; for specific technical challenges - write blog posts, give talks, mentor others in your area of expertise - build your personal brand.&lt;/li&gt;
&lt;li&gt;Always explore &quot;other options&quot; and alternatives to your preferred solutions to avoid tunnel vision (e.g., document pros and cons of different approaches), even if you are an expert in a specific area.&lt;/li&gt;
&lt;li&gt;Sharpen your problem-solving skills by practicing coding challenges, system design interviews, and architecture exercises regularly (e.g., code algorithms using different data structures in various languages; practice system design interviews with peers - see &quot;architectural katas&quot;).&lt;/li&gt;
&lt;li&gt;If you lack time for coding practice, consider a dedicated weekly slot (e.g., 1–2 hours every Friday afternoon) to focus on &lt;strong&gt;coding practice&lt;/strong&gt; and learning new technologies (timebox it; use the Pomodoro Technique to stay focused).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the AI era, leverage AI tools to assist your learning (e.g., use &lt;a href=&quot;https://notebooklm.google.com/&quot;&gt;Notebook LM&lt;/a&gt; from Google to summarize articles, generate code snippets, or explain complex concepts).&lt;/p&gt;
&lt;p&gt;And finally, transform learning into a habit - build your identity as a lifelong learner - use techniques from &quot;Atomic Habits&quot; by James Clear (habit stacking, environment design, tracking progress).&lt;/p&gt;
&lt;p&gt;Recommended readings: &lt;a href=&quot;https://www.amazon.com/Designing-Data-Intensive-Applications-Reliable-Maintainable/dp/1449373321/&quot;&gt;Designing Data-Intensive Applications: The Big Ideas Behind Reliable, Scalable, and Maintainable Systems&lt;/a&gt; by Martin Kleppmann; &lt;a href=&quot;https://martinfowler.com/articles/expert-generalist.html&quot;&gt;Expert Generalist&lt;/a&gt; by Martin Fowler; &lt;a href=&quot;https://www.amazon.com/Atomic-Habits-Proven-Build-Break/dp/1847941842/&quot;&gt;Atomic Habits: An Easy &amp;#x26; Proven Way to Build Good Habits &amp;#x26; Break Bad Ones&lt;/a&gt; by James Clear&lt;/p&gt;
&lt;h3 id=&quot;manage-your-autonomy-and-time&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#manage-your-autonomy-and-time&quot; aria-label=&quot;manage your autonomy and time permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Manage your autonomy and time&lt;/h3&gt;
&lt;p&gt;Staff Engineers have a lot of freedom - that means managing yourself well.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set clear boundaries and expectations with your manager and stakeholders regarding your availability and workload.&lt;/li&gt;
&lt;li&gt;Choose projects based on impact and alignment with your goals.
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&quot;Your ‘no’ is a gift to your future self.&quot;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Evaluate how a project affects your &lt;strong&gt;energy&lt;/strong&gt;, &lt;strong&gt;credibility&lt;/strong&gt;, &lt;strong&gt;quality of life&lt;/strong&gt;, &lt;strong&gt;social capital&lt;/strong&gt;, and &lt;strong&gt;skills growth&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Every technical skill set slowly becomes obsolete over time - invest in projects that help you grow and learn new skills (&lt;strong&gt;learning by doing&lt;/strong&gt;, working closely with someone who is highly skilled), otherwise you may end up learning in your free time (which is hard to maintain in the long term).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Use time blocking for &quot;deep work&quot;, meetings, and learning (e.g., block mornings for deep work, afternoons for meetings and collaboration, and Fridays for learning and coding practice - add everything to your calendar).&lt;/li&gt;
&lt;li&gt;Measure and &lt;strong&gt;track your &quot;deep work&quot;&lt;/strong&gt; ratio weekly - aim for 40–50% of your time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Recommended readings: &lt;a href=&quot;https://www.amazon.com/Staff-Engineers-Path-Individual-Contributors/dp/1098118731/&quot;&gt;The Staff Engineer&apos;s Path: A Guide for Individual Contributors Navigating Growth and Change&lt;/a&gt; by Tanya Reilly; &lt;a href=&quot;https://handsonarchitects.com/blog/2024/how-to-manage-your-time-as-software-engineer-5-practial-tips-and-tools/&quot;&gt;&quot;How to Manage Your Time as a Software Engineer – 5 Practical Tips and Tools&quot;&lt;/a&gt; by Handson Architects; &lt;a href=&quot;https://www.amazon.com/Deep-Work-Focused-Success-Distracted/dp/0349413681/&quot;&gt;Deep Work: Rules for Focused Success in a Distracted World&lt;/a&gt; by Cal Newport&lt;/p&gt;
&lt;h3 id=&quot;ensure-effective-project-management-and-execution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ensure-effective-project-management-and-execution&quot; aria-label=&quot;ensure effective project management and execution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Ensure effective project management and execution&lt;/h3&gt;
&lt;p&gt;Staff Engineers often lead complex, cross-team initiatives. You are responsible for predictable results.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Define clear goals, success criteria, and priorities.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://handsonarchitects.com/blog/2025/effective-planning-for-sre-projects/#use-lightweight-tooling-to-visualize-the-plan&quot;&gt;Use visual tools&lt;/a&gt; for planning and tracking progress.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://handsonarchitects.com/blog/2025/effective-planning-for-sre-projects/#break-down-the-work-into-milestones&quot;&gt;Break work into manageable milestones&lt;/a&gt; with explicit risks and dependencies.&lt;/li&gt;
&lt;li&gt;Use asynchronous updates - dashboards, weekly summaries, short reports.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://handsonarchitects.com/blog/2025/effective-planning-for-sre-projects/#communicate-the-plan-and-iterate-often&quot;&gt;Iterate plans&lt;/a&gt; based on feedback. Adapt continuously as new information emerges.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;work-effectively-with-your-engineering-manager&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#work-effectively-with-your-engineering-manager&quot; aria-label=&quot;work effectively with your engineering manager permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Work effectively with your Engineering Manager&lt;/h3&gt;
&lt;p&gt;Your manager can be your best ally if you clearly define their expectations.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Define success metrics together and review them regularly (1:1s, quarterly reviews), e.g., counting commits in performance reviews is not effective when you focus on non-coding activities like architecture, mentoring, or cross-team collaboration.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clarify your role&lt;/strong&gt; in each project (tech lead, TPM, advisor, etc.) and define responsibilities to avoid misunderstandings (like who owns communication with stakeholders, who manages timelines, etc.).&lt;/li&gt;
&lt;li&gt;Communicate progress, risks, and dependencies regularly.&lt;/li&gt;
&lt;li&gt;Don’t wait for instructions - &lt;strong&gt;shape your own path&lt;/strong&gt; and seek feedback proactively.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your manager doesn’t support your growth or align with your values - find an environment that does. The right manager can make or break your Staff Engineer journey.&lt;/p&gt;
&lt;h2 id=&quot;common-traps&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#common-traps&quot; aria-label=&quot;common traps permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Common traps&lt;/h2&gt;
&lt;p&gt;Even experienced Staff Engineers fall into these traps. Here are the ones we’ve faced ourselves:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unknown unknowns
&lt;ul&gt;
&lt;li&gt;Ambiguous problems are part of the role. Don’t rush decisions. Gather information, seek diverse perspectives, and &lt;strong&gt;iterate&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;When in doubt, prototype or &lt;strong&gt;experiment&lt;/strong&gt; to validate assumptions.&lt;/li&gt;
&lt;li&gt;Leverage your network to fill knowledge gaps: talk to architects, domain experts, TPMs, and other stakeholders to gather insights. They can provide valuable context and help you &lt;strong&gt;avoid blind spots&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Overengineering
&lt;ul&gt;
&lt;li&gt;Avoid elegant but impractical solutions. &lt;strong&gt;&quot;Good enough now&quot; beats &quot;perfect later.&quot;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Always incrementally improve - use feedback loops to refine your designs, like with TDD or iterative architecture.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Losing touch with coding
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stay hands-on! No excuses!&lt;/strong&gt; Pair programming, small coding tasks, or code reviews keep your credibility, social capital, and empathy with your team.&lt;/li&gt;
&lt;li&gt;Take part in hackathons, coding dojos, or internal tech initiatives to stay engaged with coding.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Over-specialization in proprietary (organizational) tools
&lt;ul&gt;
&lt;li&gt;Avoid being locked into internal or vendor-specific technologies. Think in terms of transferable concepts. Think like a generalist and map your knowledge to broader concepts and technologies.&lt;/li&gt;
&lt;li&gt;Check alternatives and open-source solutions to avoid vendor lock-in and ensure flexibility - this will also help you to grow your skills and stay relevant in the industry.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Measure everything. Iterate often. Keep learning.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Staff Engineer role is a rewarding path for experienced engineers who want to influence at scale without moving into management.&lt;/p&gt;
&lt;p&gt;By cultivating architectural thinking, organizational awareness, and a strong learning habit, you can drive meaningful change across teams. The best Staff Engineers measure, iterate, and lead by example - combining technical excellence with human empathy.&lt;/p&gt;
&lt;p&gt;Recommended resources to explore the role deeper:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Staff-Engineers-Path-Individual-Contributors/dp/1098118731/&quot;&gt;The Staff Engineer&apos;s Path: A Guide for Individual Contributors Navigating Growth and Change&lt;/a&gt; by Tanya Reilly&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pathfinderengineer.substack.com/&quot;&gt;Pathfinder Engineer blog&lt;/a&gt; by Piotr Stapp, Tomasz Pęczek, and Jakub Gutkowski&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Services Architecture and Code Ownership]]></title><description><![CDATA[Moving from monolith to services architecture changes not only how systems are built, but above all the way teams work together. One of the…]]></description><link>https://handsonarchitects.com/blog/2025/services-architecture-and-code-ownership/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2025/services-architecture-and-code-ownership/</guid><pubDate>Mon, 20 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/86bff856da74f93777c11d31fcc10b29/71c1d/code-ownership.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAADJElEQVR42gEZA+b8APHbsPDar/Tesvvnu+jTqrCbf7qni7qmi7yojLyojLGdgq6bgbGeg7Geg7+skMCtkcKvk76rkK2agNW9lwDw26/24bbhy6LCo3e9o3zLvKjdz7bVxav059Dn28Tw5Mzy5c3u4crw48zl2cLw5c7m2L3l2cHOwKvGrYgA9N+z3saboXI/nWk2kWU6wbKbta+graeYy8Gsr6SRqp+OppuKsKaTsqmWtqya0MWxxKdy1sGa0MOwx6+JAPnlur+hd6BsOtOmd6+LZcq9pvHmzd7SvNnNtsG1obqum8S5pMq/qpeHda+diOTZwsi7o8zAqsO1nsmyjAD34bXOt4+vhlvLoHKwjWfLu6TFuqeyp5XRxbDx5c3azrnx5Mvd0bmOakqAUjGce2DKvqnEt6K+rpjLs44A8tyw7Nuyz62Atothr4pi4M2w2s+5xLqm6dzG2sikyLaZ+OvT2smu0qVx2aVxfVExz8Gq4NbAy8CqybGLAO7bss+ofK5tPbWCVqJuRdTDqP/018eria6Sccy8o8Syke7fwNa8nLqLXuiwdreWc+7n0N7Ruq2Zgc21jwDUsoa/YybMayusYjK8XiHQkWLh07nQm2bEqIbQu5/GsZPs38Tfza+pflTElGOXk3fAtJjGnHCOcFLPuJMAm2xK3XQs0HExzm0t2HMwvGYur2o6qnRKqZuFpH9Yz5tnvqSFgIluc3ZVgHZVY3hYenxYz5xol4Bl1L2XAJdhPrphJq9zRc19Qs9uLZpkQZlzV5R4Y4VzYq6afbOOaYl1WmpuVm97Xm5/X3SDYXR/XnFwUnx0YufQpgCMXT/KaCitb0G0gFGqZzaOeWaWiHuNfXCciXe4o4KOfm+Pfm6XgnKDeGR+iGfEtp2Agmxrg2JnbWDeyaQAbFE8rmgzo2MylmVAfV5IiXZnjnhmiXJgm3NRvHtAmXpejH1vkoBxiHhrcWhUy7mgnYFegnJKe2pSwqiDAHxVM2tKMY5eNZpmOZNlPKdwP7F0QLR2P7d3Pr17QK9zP6BuQ6BxSJlvS45kQZtoOqdqNpxiNIRULZ58WTtk119uzOa2AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Code ownership hero image&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/86bff856da74f93777c11d31fcc10b29/3c492/code-ownership.png&quot;
        srcset=&quot;/static/86bff856da74f93777c11d31fcc10b29/ff46a/code-ownership.png 325w,
/static/86bff856da74f93777c11d31fcc10b29/a6d36/code-ownership.png 650w,
/static/86bff856da74f93777c11d31fcc10b29/3c492/code-ownership.png 1300w,
/static/86bff856da74f93777c11d31fcc10b29/71c1d/code-ownership.png 1536w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Moving from monolith to services architecture changes not only how systems are built, but above all the way teams work together. One of the most important principles to get right soon is ownership. Without clear ownership, services can quickly turn into hard to change, harder to scale and painful to operate distributed monoliths.&lt;/p&gt;
&lt;h2 id=&quot;why-ownership-matters&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-ownership-matters&quot; aria-label=&quot;why ownership matters permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why Ownership Matters&lt;/h2&gt;
&lt;p&gt;When a new service is created, we strongly recommend defining its clear owner from day one. Ownership is not only about maintaining code.
It is about accountability for decisions, quality and the long-term health of the service but also about empowering teams to move fast.&lt;/p&gt;
&lt;p&gt;Clearly defined ownership helps to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reduce unnecessary coordination between teams.&lt;/li&gt;
&lt;li&gt;Align accountability with decision making power.&lt;/li&gt;
&lt;li&gt;Increase autonomy and allow teams to deliver features faster.&lt;/li&gt;
&lt;li&gt;Maintain quality and consistency over time.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-software-architects-perspective&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-software-architects-perspective&quot; aria-label=&quot;the software architects perspective permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Software Architect’s Perspective&lt;/h3&gt;
&lt;p&gt;From an architect’s viewpoint, ownership means teams are empowered to make decisions without waiting for &quot;architectural sign-off&quot;. The architect does not become a bottleneck but instead acts as a guide and supporter:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Decision-making remains with the team&lt;/strong&gt;, especially senior developers who best understand the service internals.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The architect enables alignment&lt;/strong&gt;, ensuring that local decisions still fit into the big picture.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Support is always available&lt;/strong&gt;: architects should make time to coach, review, and unblock teams when needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Balancing these aspects is not easy but it allows teams to move fast with confidence while architects ensure consistency, long-term sustainability and system coherence.&lt;/p&gt;
&lt;h2 id=&quot;core-principles-of-code-ownership&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#core-principles-of-code-ownership&quot; aria-label=&quot;core principles of code ownership permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Core Principles of Code Ownership&lt;/h2&gt;
&lt;h3 id=&quot;1-strong-ownership-vs-collective-ownership&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-strong-ownership-vs-collective-ownership&quot; aria-label=&quot;1 strong ownership vs collective ownership permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Strong Ownership vs. Collective Ownership&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Collective ownership&lt;/strong&gt; assumes any developer can work on any part of the system. While appealing in theory, it often breaks down in organizations with limited services experience. It leads to inconsistent standards, quick hacks and lack of accountability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Strong ownership&lt;/strong&gt; means each service has a clear owner (preferably a team, not an individual). The owning team decides on coding standards, technology choices, deployment schedules, and operational practices. This autonomy reduces dependencies and improves productivity. As the number of services grows, strong ownership prevents coordination overhead from getting out of control.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As Sam Newman and James Lewis note: &lt;em&gt;services buy you options&lt;/em&gt;. Strong ownership ensures those options are used intentionally, not chaotically.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/0e5977a894427447ddfa9e88a994de38/c263c/ownership-model-diagram.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.61538461538461%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA+ElEQVR42p2Ry47CIBiFef8Xc2Xiyk7SWWg6Oq3a0nIvBY5cBhdmVp7m9CdwuHxATv0d95GCsQXWWtjNQjCB7tZh3+8hVoGqKfZ/XwcYYyCVgvqz0QbH4Yh2akFqOA1IKV81hIBPROy25cnpdMuyxJOyvKMPHmYrtcp7j5T/T6tbYZ0FaS89buMEzllekM4UUkic+zN2lx34yl+THgvDV3eNFDJvzDnPTvnD7wHN2IB4X9CM0aCUYp7nghw/F1yuVekWfPx55+DeHbOJhpRgyItorbNTO+F9dIcJeUjIEaG+ckYeIvLPG/JckHV8OM4FhChWUhXkR4MnZQ0ftW/W2z0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Ownership model diagram&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/0e5977a894427447ddfa9e88a994de38/3c492/ownership-model-diagram.png&quot;
        srcset=&quot;/static/0e5977a894427447ddfa9e88a994de38/ff46a/ownership-model-diagram.png 325w,
/static/0e5977a894427447ddfa9e88a994de38/a6d36/ownership-model-diagram.png 650w,
/static/0e5977a894427447ddfa9e88a994de38/3c492/ownership-model-diagram.png 1300w,
/static/0e5977a894427447ddfa9e88a994de38/c263c/ownership-model-diagram.png 1631w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/964484bc338690931cf9512054826f9b/c263c/ownership-model-diagram-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.61538461538461%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAPoAAAD6AG1e1JrAAABcElEQVR42mNwy6rwSCmr0Z4woVV8/vz5HBMnTmSf2DpRNHNiprFtj22CR6uHKAMU+GWUqntklwQeW7WKc+aqmfyLFy/mmzlzJv+jY4843Xvc/Wy7bN1gahkqKiqEQbi+vl4IiEVCQ0OZGcgBMcXF3EADmIqBdFVVlTSQLVVeXs5vX2/P4jHRgy90FcLgtLSZrKFZ9TxYzemO4fap9+FicM8pC4orqdTr7q4Xq6/vlGpubpadXDFZOK0/zdy+yz7XqdVJHKbJO69SxzWzJGb/qlU8kxcuFJ49e7bQ5MmTha9vuM7r0usSZdNqE8BgDLQVqJYR6DI+oGHyIAPb2tqEGf4zMGrVa7GBaJiBoGAwTktj/f//P9OqVauYkbHxTGNWkK/ACkFeBoUbEAt0dHTwQ9ksZIUhzMvTuruBXq7nKiws5Oyp7xEBe7nbPgfVy8U6bllF0Rd27uSevWqV0NKlSwVB3r6y/wqPS497pG27rT8ApM2Usaz6ZP4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Ownership model diagram&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/964484bc338690931cf9512054826f9b/3c492/ownership-model-diagram-dark.png&quot;
        srcset=&quot;/static/964484bc338690931cf9512054826f9b/ff46a/ownership-model-diagram-dark.png 325w,
/static/964484bc338690931cf9512054826f9b/a6d36/ownership-model-diagram-dark.png 650w,
/static/964484bc338690931cf9512054826f9b/3c492/ownership-model-diagram-dark.png 1300w,
/static/964484bc338690931cf9512054826f9b/c263c/ownership-model-diagram-dark.png 1631w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;2-ownership-and-roadmaps&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-ownership-and-roadmaps&quot; aria-label=&quot;2 ownership and roadmaps permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Ownership and Roadmaps&lt;/h3&gt;
&lt;p&gt;A service-owning team should control its backlog and roadmap. This helps prevent short-term fixes that block future improvements. Ownership is not only about fixing bugs. It is about deeply understanding the service from both a technical and business perspective and keeping it healthy over time (e.g., finding enough time to reduce technical debt).&lt;/p&gt;
&lt;h3 id=&quot;3-developer-experience-as-a-foundation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-developer-experience-as-a-foundation&quot; aria-label=&quot;3 developer experience as a foundation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Developer Experience as a Foundation&lt;/h3&gt;
&lt;p&gt;Ownership also means responsibility for making services easy to develop, test and run. As services multiply, running a full system locally becomes harder. Investing early in developer platforms, starter repositories, or lightweight local environments (e.g., Docker Compose stack) is crucial. Teams that neglect developer experience will struggle with productivity and morale.&lt;/p&gt;
&lt;h2 id=&quot;team-structures-and-boundaries&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#team-structures-and-boundaries&quot; aria-label=&quot;team structures and boundaries permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Team Structures and Boundaries&lt;/h2&gt;
&lt;p&gt;Drawing on &lt;em&gt;Team Topologies&lt;/em&gt;, ownership works best when aligned with clear team boundaries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stream-aligned teams&lt;/strong&gt; should own services end-to-end, from feature design to deployment and operations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platform teams&lt;/strong&gt; provide the tools and infrastructure to support ownership, ensuring consistency in CI/CD, observability and local development.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In smaller organizations, stream-aligned teams may dedicate members to platform-like responsibilities. Encourage those naturally interested in tooling and give them explicit time to improve it.&lt;/p&gt;
&lt;p&gt;Each stream-aligned team should have full lifecycle responsibility for its services: from code to production metrics. That lets to significantly reduce feedback loops between users and developers.
Clear boundaries reduce friction. When teams own services, they can focus on delivering business value without waiting for permission or external coordination.&lt;/p&gt;
&lt;h2 id=&quot;common-pitfalls&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#common-pitfalls&quot; aria-label=&quot;common pitfalls permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Common Pitfalls&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;No Ownership at All&lt;/strong&gt;&lt;br&gt;
Services created without clear ownership quickly become abandoned, leading to quality and operational issues.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Overlapping or Shared Ownership&lt;/strong&gt;&lt;br&gt;
If multiple teams &quot;share&quot; ownership, accountability is diluted. Problems often fall into gaps between teams.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Neglecting Developer Experience&lt;/strong&gt;&lt;br&gt;
As systems grow beyond a few services, developer experience becomes critical. Without investment, onboarding slows, testing is painful, and productivity drops.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Platform Too Late&lt;/strong&gt;&lt;br&gt;
If you expect to grow from a handful of services to dozens, invest in a common platform early. Retrofitting standards later is far more expensive.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;recommendations-for-practitioners&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#recommendations-for-practitioners&quot; aria-label=&quot;recommendations for practitioners permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Recommendations for Practitioners&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Assign ownership from day one&lt;/strong&gt; for every new service. If your codebase has a shared library, make sure it has a clear owner too.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prefer strong ownership&lt;/strong&gt;: let teams own standards, deployments, and technology choices where possible.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Align ownership with team boundaries&lt;/strong&gt; following &lt;em&gt;Team Topologies&lt;/em&gt; guidance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Invest in developer experience early&lt;/strong&gt;, even if it means dedicating partial resources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ensure each team controls its roadmap&lt;/strong&gt;, balancing short-term delivery with long-term maintainability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Document ownership explicitly&lt;/strong&gt; in a service catalog, repository README, GitHub Ownership file, or platform portal to avoid confusion as teams evolve.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;closing-reflection&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#closing-reflection&quot; aria-label=&quot;closing reflection permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Closing Reflection&lt;/h2&gt;
&lt;p&gt;Services promise flexibility and speed but only if paired with the right organizational practices. Clear ownership is one of the most powerful ways to align accountability, autonomy, and quality.&lt;/p&gt;
&lt;p&gt;Strong ownership helps teams to move fast without chaos. It reduces coordination costs, improves service quality, and makes scaling both the system and the organization sustainable.&lt;/p&gt;
&lt;p&gt;Ownership is not a process detail but rather a cornerstone of successful service architecture.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[AI Toolset for Software Architects (Q3 2025)]]></title><description><![CDATA[Artificial intelligence has become a daily companion for many software architects. In 2025, it is less about “choosing the best AI tool” and…]]></description><link>https://handsonarchitects.com/blog/2025/ai-toolset-for-software-architect-2025q3/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2025/ai-toolset-for-software-architect-2025q3/</guid><pubDate>Wed, 24 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/2bf8c09f4d2c2dd909b620112444f354/71c1d/architect-ai-toolset.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAADJElEQVR42gEZA+b8APu2aPu3av68bNmhYaR8U8aSWe+0cejJovC8fOu0c97Bm+e0d+69fs2+oey8f+XMp/fEf/+/bvy2afy3aQD5tWj/xHbJnGWNaU23fFGHYUTks3br0KjewJTYwZ/IvqvdxKHYuozBvanYuozBrY6hhmfYpGX+vG/7t2kA+bZq/8p8sodYnHNU3Kh5to5o7bZxyLyguMC52baE67p86bl72cGe4sijxJhlgFIxg1Y5blI97rRs/rlrAPq4a//HeuKjYJtxT5l6X6J6Veu4eOPNq97HpdvBnebLperCj9e8k97Fn9qrcreLZc6dcplmROCkY/+8bQD8uWv5vHHCp310ZFd2VUCLcVXuvn/z5MfAr5iqmYXZ0Lnyz5rQvZnSya3qt3S5imHQmGvSkFvvs23/u2sA+7dnqZyCfZijg4yMjn9ve5KaraKI+uG/29O639O50Max0LWNu5dnrZRx7Lp2z5RYsnJIv35J2JRM761iANamaZyjnuHNr9PGrtnMsqOnoF+BdZuahp+sm5ilkoGUmHmNkJSLdpOjpKuMYd+NPdWCNtyHN9qALteRSACki2u4gVnTvJ7e0LXizrGiq6hrf4aDdmiLoIaMqY9wj5pxio+xn4GUnpq2iFTVfzDvkTftkDjZgjLbjUAAnYZqtpV34M2w4tCz2sy1iIt+lopfkWEzo209gWxPhXNgi3dgkXRchoiBZl5XkGlI6Y414YYyw3Ms1og8APS0ZtHCpNDIstfJsL64qXZ2YsChRsCRR8NrM55kO7BnNMtsK6ppOXBqYXNrZm5jXLF0QMmBRcFzLc+JQgDek1K0flKXblKNaE6Vl4p1aVyiaz2lYzSwZzSjXi+nYDGsYjKgZz+RkoN7al1nV09vWEmFX0adXjPHfEMAy3U/zXY/0HY8wXRAcXRtpnJKv34/q2o5vG06vm46sW85sotFlnVGd3Jmo2tEvW49r2o/v3E/znpE0npDANB7Q896Q9F7Q8R2Q51pR7lwP8qIRsZ7Qsl2Qsp2Qb16QbqNRr2CQ6FhO71zQ9Z9Q9N8Q9N8Q896Qs96QvUJxQaoeB+cAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;AI architect toolset&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/2bf8c09f4d2c2dd909b620112444f354/3c492/architect-ai-toolset.png&quot;
        srcset=&quot;/static/2bf8c09f4d2c2dd909b620112444f354/ff46a/architect-ai-toolset.png 325w,
/static/2bf8c09f4d2c2dd909b620112444f354/a6d36/architect-ai-toolset.png 650w,
/static/2bf8c09f4d2c2dd909b620112444f354/3c492/architect-ai-toolset.png 1300w,
/static/2bf8c09f4d2c2dd909b620112444f354/71c1d/architect-ai-toolset.png 1536w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Artificial intelligence has become a daily companion for many software architects. In 2025, it is less about “choosing the best AI tool” and more about &lt;strong&gt;curating and evolving a living toolset&lt;/strong&gt;. No single platform covers all needs - at least not yet. Instead, we must constantly experiment, combine, and adapt AI capabilities to support architecture design, technical leadership, and delivery in ever-changing environments.&lt;/p&gt;
&lt;p&gt;This post outlines how we use AI tools in our practice, along with examples of how they support the software architect role.&lt;/p&gt;
&lt;h2 id=&quot;the-architects-role-and-ai-support&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-architects-role-and-ai-support&quot; aria-label=&quot;the architects role and ai support permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;The Architect’s Role and AI Support&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Software architects are expected to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Shape system design&lt;/strong&gt; by balancing business requirements, constraints, and technical trade-offs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Guide teams&lt;/strong&gt; in applying architectural principles and patterns across distributed, cloud-native, and DevOps contexts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bridge communication&lt;/strong&gt; between engineering, leadership, and business stakeholders.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI tools do not replace these responsibilities. Instead, they provide &lt;strong&gt;acceleration, augmentation, and reflection&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Acceleration: speeding up research, prototyping, and documentation.&lt;/li&gt;
&lt;li&gt;Augmentation: extending reasoning, generating options, and exploring trade-offs.&lt;/li&gt;
&lt;li&gt;Reflection: providing different perspectives on decisions and assumptions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;categories-of-ai-tools-for-architects&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#categories-of-ai-tools-for-architects&quot; aria-label=&quot;categories of ai tools for architects permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Categories of AI Tools for Architects&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id=&quot;1-searching--learning&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-searching--learning&quot; aria-label=&quot;1 searching  learning permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;1. Searching &amp;#x26; Learning&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.perplexity.ai/&quot;&gt;Perplexity.ai&lt;/a&gt; – quick search across sources, exploring new technologies, and summarizing new research.&lt;/li&gt;
&lt;li&gt;Useful for: learning about the latest AWS/Azure/GCP services, validating assumptions against documentation, forums, and the broader web.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;2-brainstorming--ideation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-brainstorming--ideation&quot; aria-label=&quot;2 brainstorming  ideation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;2. Brainstorming &amp;#x26; Ideation&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://chat.openai.com/&quot;&gt;ChatGPT (GPT-5)&lt;/a&gt; – rapid brainstorming, generating scenarios, and “talking through” design alternatives.&lt;/li&gt;
&lt;li&gt;Useful for: early-stage thinking, exploring edge cases, and facilitating design discussions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;3-prompt-engineering&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-prompt-engineering&quot; aria-label=&quot;3 prompt engineering permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;3. Prompt Engineering&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/features/copilot&quot;&gt;GitHub Copilot&lt;/a&gt; – helps refine prompts and supports small-scale refactoring and documentation.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://platform.openai.com/chat/edit?models=gpt-5&amp;#x26;optimize=true&quot;&gt;OpenAI Prompt Optimizer (GPT-5)&lt;/a&gt; – supports improving prompt clarity, efficiency, and outcome quality.&lt;/li&gt;
&lt;li&gt;Useful for: creating &lt;strong&gt;repeatable, reliable prompts&lt;/strong&gt; that make AI collaboration more effective across tasks.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;4-agentic-coding--prototyping&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-agentic-coding--prototyping&quot; aria-label=&quot;4 agentic coding  prototyping permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;4. Agentic Coding &amp;#x26; Prototyping&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://claude.ai/code&quot;&gt;Claude Code&lt;/a&gt; with &lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/sub-agents&quot;&gt;subagents&lt;/a&gt; – supports coding, proof-of-concept implementations, and technical design, including drafting Architecture Decision Records (ADRs). Example subagents:
&lt;ul&gt;
&lt;li&gt;Domain Driven Design Expert - focuses on discovering and identifying subdomains, categorizing them as core, supporting, or generic, and drafting bounded contexts.&lt;/li&gt;
&lt;li&gt;Code Reviewer (specialized for specific languages or frameworks) - reviews code for best practices, potential bugs, and adherence to coding standards, may use linters and static analysis tools.&lt;/li&gt;
&lt;li&gt;Trade-off Analyst - evaluates different architectural options based on criteria such as performance, scalability, cost, and maintainability, providing a balanced view of pros and cons.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Useful for: almost everything from &lt;strong&gt;quick prototyping&lt;/strong&gt; to &lt;strong&gt;detailed design documentation&lt;/strong&gt;. The key is to &lt;strong&gt;leverage subagents&lt;/strong&gt; for specialized tasks and narrow prompts context to the specific challenge.&lt;/li&gt;
&lt;li&gt;Integrated MCPs (Model Context Protocol servers):
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/oraios/serena&quot;&gt;Serena&lt;/a&gt; for querying the codebase (works especially well with bigger codebases).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/upstash/context7&quot;&gt;Context7&lt;/a&gt; for browsing documentation.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking&quot;&gt;Sequential Thinking&lt;/a&gt; for structured AI planning.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Together, these tools provide &lt;strong&gt;architecture-aware prototyping&lt;/strong&gt; and &lt;strong&gt;knowledge-driven design workflows&lt;/strong&gt;, connecting documentation, code, and work planning into a coherent flow.&lt;/p&gt;
&lt;h2 id=&quot;list-to-explore-next&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#list-to-explore-next&quot; aria-label=&quot;list to explore next permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;List to Explore Next&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;The landscape evolves quickly. Here are tools we plan to explore next:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://openai.com/codex/&quot;&gt;OpenAI Codex&lt;/a&gt; – potential alternative to Claude Code.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.warp.dev/&quot;&gt;Warp&lt;/a&gt; – positioning itself as an “Agentic Development Environment.” Worth comparing with Claude Code and Codex.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.firecrawl.dev/mcp-server&quot;&gt;Firecrawl MCP&lt;/a&gt; – expanding external data integration for agent workflows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parallel agent workflows&lt;/strong&gt; – experimenting with multiple agents working on subtasks simultaneously.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This exploration list serves as a reminder: &lt;strong&gt;an architect’s AI toolset is never static&lt;/strong&gt;. Staying curious and testing new capabilities is part of the job.&lt;/p&gt;
&lt;h2 id=&quot;practical-applications-in-daily-work&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#practical-applications-in-daily-work&quot; aria-label=&quot;practical applications in daily work permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Practical Applications in Daily Work&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Here is how these categories play out in real projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Architecture Exploration&lt;/strong&gt;: Use Perplexity to scan for recent practices or solutions (e.g., managed database services in AWS) or quickly validate knowledge (e.g., the &lt;code class=&quot;language-text&quot;&gt;TESTCONTAINERS_RYUK_DISABLED&lt;/code&gt; flag), and ChatGPT to brainstorm architectural options before deeper design sessions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prompt Quality&lt;/strong&gt;: Refine prompts with Copilot or the GPT-5 Prompt Optimizer to achieve more consistent outcomes when generating documents or test suites.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rapid Prototyping&lt;/strong&gt;: Pair Claude Code with MCPs to validate feasibility through quick proofs of concept, then decide if the pattern is viable for production.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation at Scale&lt;/strong&gt;: Generate ADR drafts and architecture overviews with Claude Code (especially using dedicated subagents who are experts in trade-off analysis).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Planning as first-class work&lt;/strong&gt;: Break down complex tasks into manageable steps, then use agents to work through them iteratively, ensuring alignment with architectural goals.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Best engineering practices matter more than ever&lt;/strong&gt;: AI tools can introduce errors or misinterpretations. When working with AI-generated code, practices such as linting, formatting, maintaining test coverage, conducting code reviews, and enforcing CI become even more critical.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;keep-the-toolset-alive&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#keep-the-toolset-alive&quot; aria-label=&quot;keep the toolset alive permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Keep the Toolset Alive&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;In 2025, there is no perfect or final AI toolset for software architects. Instead, the practice is about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Continuous evolution&lt;/strong&gt;: adopt, test, and discard tools as needs change.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Balancing categories&lt;/strong&gt;: ensure coverage across searching, brainstorming, prompting, coding, and documentation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Architect mindset first&lt;/strong&gt;: tools augment judgment; they do not replace it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The most effective architects will not only master today’s AI assistants but also develop &lt;strong&gt;an experimentation habit&lt;/strong&gt; - treating AI tools as evolving collaborators in the craft of system design.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;References and resources:&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.anthropic.com/&quot;&gt;Anthropic documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://platform.openai.com/docs/guides/agents&quot;&gt;OpenAI Agents API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=tcZ3W8QYirQ&quot;&gt;GPT-5 Agentic Coding with Claude Code (YouTube)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[If you don't know how to name your technique, use "test"]]></title><description><![CDATA[In software development, naming is often harder than writing the code itself. We've all been there... Struggling to find a name that is…]]></description><link>https://handsonarchitects.com/blog/2025/if-you-dont-know-how-to-name-your-technique-use-test/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2025/if-you-dont-know-how-to-name-your-technique-use-test/</guid><pubDate>Tue, 09 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/aa7c3e3ecd440796f9b21fcb7087284e/ec469/technique-naming-test.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 64%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIEA//EABUBAQEAAAAAAAAAAAAAAAAAAAID/9oADAMBAAIQAxAAAAGjSSqaQQR//8QAGxAAAgIDAQAAAAAAAAAAAAAAAQIAMgMSISL/2gAIAQEAAQUCHRVNzMVmXzP/xAAWEQEBAQAAAAAAAAAAAAAAAAAAERL/2gAIAQMBAT8BrT//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEv/aAAgBAgEBPwFL/8QAGBAAAwEBAAAAAAAAAAAAAAAAABFRICH/2gAIAQEABj8CnBsuP//EABoQAQADAQEBAAAAAAAAAAAAAAEAESFBMVH/2gAIAQEAAT8haZlODWJR6P3kCsrHZvQOns2Nh1o9gE//2gAMAwEAAgADAAAAEHcf/8QAFhEBAQEAAAAAAAAAAAAAAAAAAREQ/9oACAEDAQE/ECi5/8QAFhEBAQEAAAAAAAAAAAAAAAAAAREQ/9oACAECAQE/EEjHP//EABsQAQEBAQEAAwAAAAAAAAAAAAERIQBBMWFx/9oACAEBAAE/EEaaQFezb00W1QX6Q52q0g6KkQgrvo+c3ykuC/reESGd/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Technique naming with test&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/aa7c3e3ecd440796f9b21fcb7087284e/01ab0/technique-naming-test.jpg&quot;
        srcset=&quot;/static/aa7c3e3ecd440796f9b21fcb7087284e/c989d/technique-naming-test.jpg 325w,
/static/aa7c3e3ecd440796f9b21fcb7087284e/6aca1/technique-naming-test.jpg 650w,
/static/aa7c3e3ecd440796f9b21fcb7087284e/01ab0/technique-naming-test.jpg 1300w,
/static/aa7c3e3ecd440796f9b21fcb7087284e/ec469/technique-naming-test.jpg 1444w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In software development, naming is often harder than writing the code itself. We&apos;ve all been there... Struggling to find a name that is clear, accurate and expressive enough to capture what a technique or pattern actually does.&lt;/p&gt;
&lt;p&gt;Today we&apos;re going to roast naming conventions that use the word &quot;test&quot; in ways that can be misleading. It&apos;s a common shortcut that can lead to confusion about the true purpose of a technique.&lt;/p&gt;
&lt;p&gt;It happens more often than you&apos;d think and often, the resulting names are a bit misleading. Let&apos;s look at two well-known examples.&lt;/p&gt;
&lt;h2 id=&quot;test-driven-development&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#test-driven-development&quot; aria-label=&quot;test driven development permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Test-Driven Development&lt;/h2&gt;
&lt;p&gt;Test-Driven Development (TDD) sounds like a testing methodology, but its purpose goes much deeper.&lt;/p&gt;
&lt;p&gt;While tests are certainly written, the practice is more about incremental design. Writing a failing test first helps clarify intent, drive better structure, and guide implementation. The test is not the goal - it&apos;s the mechanism for designing code in small, verifiable steps.&lt;/p&gt;
&lt;p&gt;In practice, TDD helps developers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Think about edge cases early&lt;/li&gt;
&lt;li&gt;Write modular and testable code&lt;/li&gt;
&lt;li&gt;Keep implementation minimal and focused&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So despite the name, TDD isn&apos;t centered on testing - it&apos;s about designing with feedback.&lt;/p&gt;
&lt;h2 id=&quot;contract-testing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#contract-testing&quot; aria-label=&quot;contract testing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Contract Testing&lt;/h2&gt;
&lt;p&gt;Another good example is Contract Testing, particularly in the context of Consumer-Driven Contracts (CDC).&lt;/p&gt;
&lt;p&gt;The name suggests that we&apos;re testing a contract between two services - and yes, technically, we are. But the real power of CDC is in what it enables: communication between independently deployed teams.&lt;/p&gt;
&lt;p&gt;When a consumer&apos;s expectations change, the contract test fails - which acts as a trigger. But that failure isn&apos;t the outcome - the conversation that follows is. It leads to negotiated, versioned, and safer evolution of APIs.&lt;/p&gt;
&lt;p&gt;In short: CDC is less about &quot;testing contracts&quot; and more about managing change through collaboration.&lt;/p&gt;
&lt;h2 id=&quot;final-thought&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#final-thought&quot; aria-label=&quot;final thought permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Final thought&lt;/h2&gt;
&lt;p&gt;So why does &quot;test&quot; keep showing up in places where the core idea isn&apos;t actually testing?&lt;/p&gt;
&lt;p&gt;Likely because it gives the technique a sense of rigor. It suggests verification, structure, and confidence - all things teams care about.&lt;/p&gt;
&lt;p&gt;And that&apos;s not necessarily a bad thing. But it&apos;s worth being aware of the distinction between what the name implies and what the technique actually delivers.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Partitioning of Key-Value Data in Cache Systems (Part 3)]]></title><description><![CDATA[In the first two articles (Part 1, Part 2) of this series, we looked at why it's important to split (or partition) cache data when handling…]]></description><link>https://handsonarchitects.com/blog/2025/partitioning-of-key-value-data-in-cache-systems-part-3/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2025/partitioning-of-key-value-data-in-cache-systems-part-3/</guid><pubDate>Tue, 29 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/f602efab2b87ab676b16e4f5a7c518bf/2bef9/cache-hero-image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEY0lEQVR42mP41iz2tUkMSH5rFv8KRE0QUgzKgIiA2GJfGkSB3G8wBGQzgHQ2iX1vBpFfsSGg1PsGsSc1YherZR9UiX1sEPsCMbpJjAGi53296JdGkDq4ni9NEEViHxvFnlWJXJ2k/+6Q7/VpBk8qhL9BnQbW/Lxa5HWt6MNKkZe1op8aQWZDNH8CMz43ib2uFjkzyfDAIudrs0welgt/bxH/AdbPANT2rFoU5PgW8U9N4t9aJf53SXwBhYL4n3bxD43in5vFvzeLXquR6Swzu1Qt+7lB9F29+PMasM2fGkXf1gFDQuxYtlCnN0+nD+/mJKHfbeJAs9YnCLV6CrR68J/PF3lYJd4YrPS8TuJ5Dciyz03i98pFGYCW/O8UXx4raK7K7aDDJyLIFqDP+bdDYl6YYIWTwIQgkVVpCh1e/B8bRF83SjyvFX9UKQr03es60Rc1ogzASPrRKq4jyVQbJHeuWX9Ple7UKCmgz7s8+bqDpY72eezo8l5aYvG9nv9chdyFcpnfLSKQgARqZPjRIvaxSTzVXrA+Vu/osrzVrT4H5iS/qxNZnyTWFqZwcnHaqkb3VWXmP5uEdhUpTUtTv1oh9bNZ9BMktIH4Z5v48jSZKckax/rc52TpzcrU/9UqdqlS5na71rZKg/n5xodL5D83iV6qV9tdrXejUup/u8jfNrFfLUCbm8WASudGi+rJc+nJsqnLcDqqsv1vF3tVJ96VZb66QLshSmt9ie7/Zv7HDTKHa7SvlordKJc4USj5oEqc4VWdaJGPXKCzRr6XtIY0Ow8fR56LyLsmieyqFJuKxoZSv7xcf9f6lgVVDvWhypHe+lEe2iFu2va2OiuTpBmmRctYWGi52ajFeWvHeqg622qG2CnUZTt49UwOaG31rG/yrG1wa+2xLKvzcDH0d9X3czUAkr5O2ldKxBhSPZQ8HLQ8bdU87LU8HbT8nLWdzeS9ksK9OvtdWnq8uybatUzIq0kuSrD2ctYPctcPcNX1dtZL8VIFhgKDp62qo6WqnYUq0DCgtXYW6s6Wypb2RlF5cWWVMfkdJdPqvHeXqrYESrs7GwS66ga76bk56jeHKLyuEWZwtdXwtlFO9lL1c1SvitCqDFP1tpZfmqtztlH7UJXGvkqdvRWa05JVvJ20fV31vZz1XOy0bSw0WvwkNieIMpjoytZHaT7uM7zYZnSuzehgg35HhMLsBIWZqWotUcr+1vKWBrJGOtI2Ziqu5oqBljJJ9lJlrmJrYsV3pEgyzI8QmxostjhGYk60VImPXLanfKSN9PxwseUx4kujxFfHSSyNkezxF58QILo8RmJDvPi2ZIntKRIrY8VXRIsx7EiV3JoksTlRAkiuiRVbEAE0VWJnquTOVKkdqVKbkiS3JEmsixdfHQs0S2xGiGi7lxAwzWdZ84XoczMATQJqA6rYDEKS25MlV8VKzA4VnRYkMtFfuNdXqNldsNiOL96Ex12Dw1KRXVWMVUKQRVSQjZuLGQAYG+GV3l9OZQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cache hero image&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/f602efab2b87ab676b16e4f5a7c518bf/2bef9/cache-hero-image.png&quot;
        srcset=&quot;/static/f602efab2b87ab676b16e4f5a7c518bf/ff46a/cache-hero-image.png 325w,
/static/f602efab2b87ab676b16e4f5a7c518bf/a6d36/cache-hero-image.png 650w,
/static/f602efab2b87ab676b16e4f5a7c518bf/2bef9/cache-hero-image.png 1024w&quot;
        sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In the first two articles (&lt;a href=&quot;https://handsonarchitects.com/blog/2025/partitioning-of-key-value-data-in-cache-systems/&quot;&gt;Part 1&lt;/a&gt;, &lt;a href=&quot;https://handsonarchitects.com/blog/2025/partitioning-of-key-value-data-in-cache-systems-part-2/&quot;&gt;Part 2&lt;/a&gt;) of this series, we looked at why it&apos;s important to split (or partition) cache data when handling large amounts of information. We also talked about common problems like hotspot issues and explained how consistent hashing can help solve them. Then, we compared different ways to connect caching systems to applications - using built-in cache libraries, sidecar proxies, and centralized proxy setups - each with their own pros and cons. These articles gave a solid theoretical background that’s helpful for &lt;a href=&quot;https://martinfowler.com/articles/expert-generalist.html&quot;&gt;Expert Generalists&lt;/a&gt; and software architects to understand key ideas about caching, consistent hashing, and system design choices for managing cache.&lt;/p&gt;
&lt;p&gt;In this article, we’ll show how to put the ideas from the previous posts into practice with a real-world example. We&apos;ll use Twemproxy (also called Nutcracker) as a caching proxy for Memcached, and apply Ketama consistent hashing to spread cache keys evenly across several Memcached instances. This hands-on example will demonstrate how to build a scalable caching setup in Kubernetes, giving a more practical and &lt;a href=&quot;https://martinfowler.com/articles/expert-generalist.html#:~:text=specialists%20and%20generalists.-,Specialists,-are%20seen%20as&quot;&gt;specialist&lt;/a&gt; view of the concepts we discussed earlier.&lt;/p&gt;
&lt;p&gt;You can find the full source code on GitHub at &lt;a href=&quot;https://github.com/handsonarchitects/memcached-example&quot;&gt;github.com/handsonarchitects/memcached-example&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We’ll begin by explaining what Twemproxy is and how it works as part of a caching system. Then, we’ll walk through how to set it up with Memcached and Ketama consistent hashing. Finally, we’ll show you how to deploy everything in Kubernetes and test that it works as expected.&lt;/p&gt;
&lt;h2 id=&quot;what-is-twemproxy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-twemproxy&quot; aria-label=&quot;what is twemproxy permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is Twemproxy?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/twitter/twemproxy&quot;&gt;Twemproxy&lt;/a&gt;, also called Nutcracker, is a fast and lightweight open-source tool made by Twitter. It acts as a middle layer between your app and cache systems like Memcached or Redis. Twemproxy makes it easier to manage multiple cache servers by giving your app one simple way to connect to them all.&lt;/p&gt;
&lt;p&gt;Let’s go over the main features of Twemproxy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Single access point for caching&lt;/strong&gt;: Twemproxy gives your application one entry point to talk to many cache servers. This makes your code cleaner and easier to manage. It supports both Memcached and Redis protocols, so you can switch between them with only small changes in your code. Just keep in mind that Memcached and Redis protocols are not the same, so your app still needs to know which one it’s using.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Key partitioning&lt;/strong&gt;: Twemproxy automatically spreads cache keys across multiple servers to balance the load and avoid performance issues (hotspots). It supports several consistent hashing methods like ketama (the most common), fnv1a_64, murmur3_32, and modula (a basic modulo approach). These are built into Twemproxy and easy to use, so developers can focus on their app instead of hashing logic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Handles changing setups&lt;/strong&gt;: If you add or remove cache servers, Twemproxy updates itself automatically - no need to change your app code. It updates its internal routing (hash ring) and reassigns keys to the right servers. This makes it easy to scale the cache or replace failing nodes without disruption.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Built-in fault handling&lt;/strong&gt;: Twemproxy monitors the health of each cache server. If one becomes unhealthy, it removes that server from the active pool so no new requests go to it. The keys are then automatically rerouted to healthy servers using consistent hashing, helping your system stay available.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Efficient with resources&lt;/strong&gt;: Twemproxy reuses a small set of persistent connections to each backend server instead of creating a new one for every request. This reduces connection overhead and improves performance - especially useful when Twemproxy is used as a centralized dedicated caching layer in front of many servers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stateless by design&lt;/strong&gt;: Twemproxy doesn’t store any data itself. It just passes requests between your app and the cache. This makes it easy to scale and manage. Because it’s stateless, you can run it either as a sidecar container next to your app in Kubernetes or as a separate service - both options work well.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These features make Twemproxy a strong tool for managing cache clusters, especially in large distributed systems. Since it’s stateless and handles changing topologies well, you can use it in many ways - like as a sidecar in Kubernetes or as a standalone proxy layer.&lt;/p&gt;
&lt;p&gt;In this article, we’ll focus on the sidecar deployment in Kubernetes, which is a common approach for managing caching in modern cloud-native applications.&lt;/p&gt;
&lt;h2 id=&quot;how-to-configure-twemproxy-as-a-sidecar-container-in-kubernetes&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-configure-twemproxy-as-a-sidecar-container-in-kubernetes&quot; aria-label=&quot;how to configure twemproxy as a sidecar container in kubernetes permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to configure Twemproxy as a sidecar container in Kubernetes&lt;/h2&gt;
&lt;p&gt;In the previous article, we discussed the pros and cons of running a caching proxy as a sidecar - covering deployment complexity, scalability, and operational overhead. In this post, we will focus on two key benefits of the sidecar approach: easier network setup and automatic scaling.&lt;/p&gt;
&lt;p&gt;When we deploy Twemproxy as a sidecar container (a container running alongside the application container inside the same Kubernetes Pod), we use the local network interface to connect to the caching proxy. This lets the application talk to Twemproxy using &lt;code class=&quot;language-text&quot;&gt;localhost&lt;/code&gt;, which makes network configuration simpler and reduces latency. There are no extra network hops or DNS lookups involved.&lt;/p&gt;
&lt;p&gt;Since the sidecar runs in the same Pod as the application, it scales together with the application. When the application Pod scales up or down, the Twemproxy sidecar scales too, keeping the caching proxy layer aligned with the application instances. This setup means each application Pod has its own Twemproxy, so multiplexing connections to the cache is limited. However, this trade-off is often worth it for the simplified networking and automatic scaling benefits.&lt;/p&gt;
&lt;p&gt;The diagram below shows the architecture of our &lt;a href=&quot;https://github.com/handsonarchitects/memcached-example&quot;&gt;example application&lt;/a&gt;, illustrating how Twemproxy runs as a sidecar container in a Kubernetes Pod next to the application container:&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/e745675b15d812a961348036a5044453/0f7bd/twemproxy-sidecar-diagram.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.84615384615384%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB40lEQVR42m1S30vbUBTOi//L/oXBHroHH32Soe6pTnzwQQRBUXySIcyh+AsKljFZFYoIFSriL7rOjVkZbVertiq1StbERGNqm9wmNzf3xpukZrXbeTr34/vOOd+5h7GegjyFB3h4U+LljPfGmGCMETKnVzID0z+vRCOe5AJrJw7VMjGxCIrn9NkdsJvVHaxB7BYrV0D78Javb/Mgr86H0/7xr8iAmqaBmmYRuBhTuwKV+V3gVmRoN12nlawbCSRzIgDg7djeq96NdBF+juZ7J75RmmmalEaTGiTsLarpxDXFFIvFUGgZQZDKC8HIER3/uCDvp0qCVOXv1LPrsuMIO2LrQyzQ9sn/5deaDRLClEqlxOHhg3x7cikvRY8pmC3c/8jwZcUQ7kH+ubgzNNAy9GJkc8oRY8ZzK1f1wp+yooA3Izsve6LpSxhcP333Pk5pdGyXAzTIcpKmGfWxSUPYJR7UjtHt1v6txLm6EP7dbS9Mtxemapalr34UhnzXy+NCfWFN/4xMPBfODs4krkT4Pc0H13Pu2AjZC4ssCIOvL8KTN/8RU5Lb30RIlu4QQk1Hgk2sVGsO6/mRUGMQwvq1EPt73HC3ReoIggb0PP4V/xvUqiiKLMtyHMfzvCRJhmE0jEkeAWctjBxmXLB+AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A Twemproxy sidecar that serves as a proxyfor all cache instance nodes&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/e745675b15d812a961348036a5044453/3c492/twemproxy-sidecar-diagram.png&quot;
        srcset=&quot;/static/e745675b15d812a961348036a5044453/ff46a/twemproxy-sidecar-diagram.png 325w,
/static/e745675b15d812a961348036a5044453/a6d36/twemproxy-sidecar-diagram.png 650w,
/static/e745675b15d812a961348036a5044453/3c492/twemproxy-sidecar-diagram.png 1300w,
/static/e745675b15d812a961348036a5044453/d544a/twemproxy-sidecar-diagram.png 1950w,
/static/e745675b15d812a961348036a5044453/0f7bd/twemproxy-sidecar-diagram.png 2540w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/5777c616bd769a5ccbdaa0b684aad2c6/0f7bd/twemproxy-sidecar-diagram-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 61.84615384615384%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAPoAAAD6AG1e1JrAAACuUlEQVR42mNgQAVMxsbGrCAMZDOCBP7X1zPVAzGIDxUHsRns7e1Z4GoQcqggNHQVs3FaGquxTxpXcN25Xu+SAwe5NWPyAqtOBgbVnGyFqKpnsq/fDzKMQchrfi6vx4Ld/J7zyyBy/xnRDAxlBtF5rXvEo9pvnfAuO/qT3yh1U1TL2faQhksHS5P8eDs6OvgzK6YJ/v/PwCTkt3QZX8DG/3zeyzbBDfz//z/T/fv3OUDcoOqj8sGVh5zbp00TDG24dMKn7Oh/To3YlQE1J6sDa88cNGZgYLWysuKVt7fnCGVgYOZRmykibDfPlFupXiw0lAHsGIYPHz4oPX/5MvH//zOsUc1n7WKbztSCxAPqz5sF1xwOMvcrNg+vP64QWHXMCCQuaWzMpaWlxQNksjX9WFzd9n/JlpY/SxLA7vsPdOHXr1+lnzx5afX//z8+r+JD5pF1x6vBBtaetPQvPRSgZJGmG1S8Vz646ogJSFxcT49by16LR56BgaPmw6INTb+W/2/4vrQbHLr/wZEHNRkUhpWHRMPK9+rX98wUCWu6etq34sR/Ds3Y1SE1p+qDas8eBnlZRUWFD4jZQbGdW7+Yr6hnk0Z09EQ+ewYGFnhkwA2ERcoEYKS03TwKdPEPfv2UjVEt51tD6y8cqq8P5Zk4cSJffeFsofr/DGxxGucmRSmd/RCtdnY+1lgGmsgMSl+hoYWcofWX23zLDu/k0U7ICqg54xMEdCXYW/X1LPWhq9hA7Hjtcx0xGmffx2qcmYYz2Xh4eLDDEun//ftZnp07JAp0FTssMcMSOQMwVuuB6XFyxTphe/t6FmhCRwUzZ85kLS4u5gYymUGJHISBBrDBMEjjqlVgMVDuYQOpX7WqlxPEx2ogLgA0hGfLli3yGzZsUF+zZo3qunXrlIFsqW3btrEj+Y9ZXt6eAwC1oRfxSwnOBgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A Twemproxy sidecar that serves as a proxyfor all cache instance nodes&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/5777c616bd769a5ccbdaa0b684aad2c6/3c492/twemproxy-sidecar-diagram-dark.png&quot;
        srcset=&quot;/static/5777c616bd769a5ccbdaa0b684aad2c6/ff46a/twemproxy-sidecar-diagram-dark.png 325w,
/static/5777c616bd769a5ccbdaa0b684aad2c6/a6d36/twemproxy-sidecar-diagram-dark.png 650w,
/static/5777c616bd769a5ccbdaa0b684aad2c6/3c492/twemproxy-sidecar-diagram-dark.png 1300w,
/static/5777c616bd769a5ccbdaa0b684aad2c6/d544a/twemproxy-sidecar-diagram-dark.png 1950w,
/static/5777c616bd769a5ccbdaa0b684aad2c6/0f7bd/twemproxy-sidecar-diagram-dark.png 2540w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Each application Pod contains two containers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The application container, which exposes a REST API to interact with the cache.&lt;/li&gt;
&lt;li&gt;The Twemproxy sidecar container, which acts as a caching proxy and manages all communication between the application and the cache instances.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The application talks to Twemproxy on &lt;code class=&quot;language-text&quot;&gt;localhost:11211&lt;/code&gt;, the default Memcached port. You can scale the application Pods horizontally by running multiple replicas, allowing load balancing and high availability. Each replica has its own Twemproxy sidecar, ensuring the caching proxy layer stays consistent across all application instances.&lt;/p&gt;
&lt;p&gt;The cache instances are deployed as a StatefulSet, which gives each instance a stable network identity and persistent storage. This allows Twemproxy to use &lt;strong&gt;fixed hostnames&lt;/strong&gt; for the cache servers, keeping the caching layer stable even if instances scale up or down.&lt;/p&gt;
&lt;p&gt;In this design, Twemproxy handles all cache partitioning and provides a single cache interface to the application. The application doesn’t need to know how the cache is split, which makes maintenance and scaling easier.&lt;/p&gt;
&lt;p&gt;Next, let’s see how to configure Twemproxy to work with Memcached and Ketama consistent hashing in Kubernetes.&lt;/p&gt;
&lt;h2 id=&quot;high-availability-memcached-cluster&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#high-availability-memcached-cluster&quot; aria-label=&quot;high availability memcached cluster permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;High-Availability Memcached Cluster&lt;/h2&gt;
&lt;p&gt;To build a high-availability Memcached cluster in Kubernetes, we use a StatefulSet to manage Memcached instances. StatefulSets provide stable network names and persistent storage, allowing the cluster to scale while keeping data consistent. We deploy the Memcached StatefulSet with a Helm chart, which simplifies deployment and configuration.&lt;/p&gt;
&lt;p&gt;The Helm chart is available at &lt;a href=&quot;https://github.com/bitnami/charts/tree/main/bitnami/memcached&quot;&gt;bitnami/charts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In our example, we deploy two Memcached replicas using a StatefulSet. This StatefulSet uses a headless service so each Memcached instance has a stable hostname like &lt;code class=&quot;language-text&quot;&gt;memcached-cluster-0.memcached-cluster&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;memcached-cluster-1.memcached-cluster&lt;/code&gt;. Twemproxy uses these hostnames to configure the cache topology.&lt;/p&gt;
&lt;p&gt;Deploy the Memcached StatefulSet with this Helm command:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; memcached-cluster &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--set&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;architecture&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;high-availability&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--set&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;replicaCount&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  oci://registry-1.docker.io/bitnamicharts/memcached&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;configuring-the-twemproxy-sidecar-container&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#configuring-the-twemproxy-sidecar-container&quot; aria-label=&quot;configuring the twemproxy sidecar container permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Configuring the Twemproxy sidecar container&lt;/h2&gt;
&lt;p&gt;Twemproxy needs a configuration file (usually &lt;code class=&quot;language-text&quot;&gt;nutcracker.conf&lt;/code&gt;) to define the cache topology, including cache servers, hashing algorithm, and other settings. This file is typically in YAML format.&lt;/p&gt;
&lt;p&gt;In our example, the configuration is generated dynamically in Dockerfile&apos;s entrypoint script using environment variables passed to the Twemproxy container. This lets us change the cache setup without changing or redeploying the application. See the &lt;a href=&quot;https://github.com/handsonarchitects/memcached-example/blob/main/modules/memcached-sidecar/Dockerfile&quot;&gt;Dockerfile&lt;/a&gt; for details on how this works.&lt;/p&gt;
&lt;p&gt;After processing environment variables, the config looks like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;memcached&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 127.0.0.1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;11211&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fnv1a_64&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;distribution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ketama&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;auto_eject_hosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2000&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;server_retry_timeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2000&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;server_failure_limit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;servers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; memcached&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cluster&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;0.memcached&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cluster&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;11211&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; memcached&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cluster&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;1.memcached&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cluster&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;11211&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;servers&lt;/code&gt; list defines the Memcached hostnames, ports, and weights for key distribution. &lt;code class=&quot;language-text&quot;&gt;hash&lt;/code&gt; sets the hashing algorithm, &lt;code class=&quot;language-text&quot;&gt;distribution&lt;/code&gt; controls how keys are spread across instances, and &lt;code class=&quot;language-text&quot;&gt;auto_eject_hosts&lt;/code&gt; lets Twemproxy remove unhealthy servers automatically.&lt;/p&gt;
&lt;p&gt;You can update the cache cluster by changing the &lt;code class=&quot;language-text&quot;&gt;SERVERS&lt;/code&gt; environment variable in the Twemproxy container, without touching application code or redeploying:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; SERVERS
    &lt;span class=&quot;token key atrule&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; memcached&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cluster&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;0.memcached&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cluster&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;11211&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;memcached&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cluster&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;1.memcached&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cluster&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;11211&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;configuring-the-application-container&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#configuring-the-application-container&quot; aria-label=&quot;configuring the application container permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Configuring the Application Container&lt;/h2&gt;
&lt;p&gt;Our application uses &lt;code class=&quot;language-text&quot;&gt;pymemcache&lt;/code&gt; (a Python Memcached client) to talk to Twemproxy. It reads the &lt;code class=&quot;language-text&quot;&gt;MEMCACHED_HOST&lt;/code&gt; environment variable to know where to connect:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; base&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Client&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getenv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;MEMCACHED_HOST&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;localhost&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11211&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The application container is configured with:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; MEMCACHED_HOST
  &lt;span class=&quot;token key atrule&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; localhost&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This points the app to the Twemproxy sidecar on the local Pod network.&lt;/p&gt;
&lt;h2 id=&quot;application-pod-specification&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#application-pod-specification&quot; aria-label=&quot;application pod specification permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Application Pod Specification&lt;/h2&gt;
&lt;p&gt;Here’s an example Kubernetes Pod spec including both containers:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; handsonarchitects/memcached&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;api
    &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; MEMCACHED_HOST
        &lt;span class=&quot;token key atrule&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 127.0.0.1
    &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; handsonarchitects/memcached&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;sidecar
    &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; SERVERS
        &lt;span class=&quot;token key atrule&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; memcached&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cluster&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;0.memcached&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cluster&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;11211&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;memcached&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cluster&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;1.memcached&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cluster&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;11211&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This spec is part of the deployment manifest &lt;a href=&quot;https://github.com/handsonarchitects/memcached-example/blob/main/k8s/memcached-api-deployment.yaml&quot;&gt;k8s/memcached-api-deployment.yaml&lt;/a&gt;, which controls scaling and replication.&lt;/p&gt;
&lt;h2 id=&quot;testing-and-observability&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#testing-and-observability&quot; aria-label=&quot;testing and observability permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Testing and Observability&lt;/h2&gt;
&lt;p&gt;To test Twemproxy distributing cache keys across Memcached instances, we created a Kubernetes job that runs a Bash script. The script generates cache keys and uses curl to call the app’s REST API in parallel. This helps test the cache layer under load. The job definition is in &lt;a href=&quot;https://github.com/handsonarchitects/memcached-example/blob/main/k8s/tools-cache-generator-job.yaml&quot;&gt;k8s/tools-cache-generator-job.yaml&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After the job finishes, check app Pod logs to confirm cache keys were created and stored. Then, check cache stats on each Memcached instance using the stats command via telnet.&lt;/p&gt;
&lt;p&gt;Use this command to connect and check stats:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl run get-memcached-stats &lt;span class=&quot;token parameter variable&quot;&gt;--image&lt;/span&gt; nginx:alpine &lt;span class=&quot;token parameter variable&quot;&gt;--restart&lt;/span&gt; Never &lt;span class=&quot;token parameter variable&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--command&lt;/span&gt; -- &lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; telnet://memcached-cluster-0.memcached-cluster:11211&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Replace &lt;code class=&quot;language-text&quot;&gt;memcached-cluster-0&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;memcached-cluster-1&lt;/code&gt; to check the other instance.&lt;/p&gt;
&lt;p&gt;Inside the telnet session, run &lt;code class=&quot;language-text&quot;&gt;stats&lt;/code&gt; to see cache stats, and &lt;code class=&quot;language-text&quot;&gt;quit&lt;/code&gt; to exit.&lt;/p&gt;
&lt;h2 id=&quot;source-code-and-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#source-code-and-example&quot; aria-label=&quot;source code and example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Source code and example&lt;/h2&gt;
&lt;p&gt;The full example code, including Twemproxy sidecar setup, Memcached StatefulSet deployment, and the simple Python API, is available on GitHub: &lt;a href=&quot;https://github.com/handsonarchitects/memcached-example&quot;&gt;github.com/handsonarchitects/memcached-example&lt;/a&gt;. Clone and follow the &lt;code class=&quot;language-text&quot;&gt;README&lt;/code&gt; to deploy it in your Kubernetes cluster.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;This is the third article in our caching series focused on large-scale, data-intensive applications. Here, we showed how to build a scalable caching layer using Twemproxy, Memcached, and Ketama consistent hashing in Kubernetes. We covered the benefits of running Twemproxy as a sidecar, including easier network setup, automatic scaling, and dynamic cache management. We also demonstrated how to deploy a high-availability Memcached cluster with StatefulSets and how to configure Twemproxy to work with it. Finally, we provided a simple app example and tested the caching layer under load.&lt;/p&gt;
&lt;p&gt;We hope this practical guide, together with our previous articles (&lt;a href=&quot;https://handsonarchitects.com/blog/2025/partitioning-of-key-value-data-in-cache-systems/&quot;&gt;Part 1&lt;/a&gt;, &lt;a href=&quot;https://handsonarchitects.com/blog/2025/partitioning-of-key-value-data-in-cache-systems-part-2/&quot;&gt;Part 2&lt;/a&gt;), helps you build better caching solutions for cloud-native applications.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Partitioning of Key-Value Data in Cache Systems (Part 2)]]></title><description><![CDATA[In the previous article, we introduced cache partitioning and highlighted its advantages for scaling large applications. We also explained…]]></description><link>https://handsonarchitects.com/blog/2025/partitioning-of-key-value-data-in-cache-systems-part-2/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2025/partitioning-of-key-value-data-in-cache-systems-part-2/</guid><pubDate>Tue, 08 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/10be62f31a62bf19ff647a76a19125e1/2bef9/cache-hero-image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEiElEQVR42i1Se0wTdxy/ZCb+sag8hlyh7bV3vzvKOp6lvCwghUKLffF+rGihUITyGKOBgoBt6ZuHQAhZnDiHOsEsSqJOo2a4zSXKxnQOt2XLdG4OBDYdIkxA7H7nlnzud9/73X1/n8f3kFUX+Y+LXPdQ625qDcJDY/0V4ON/+y+6qY3u/1cI+MGqC4JE4G3ZSc5biHkrWOwCcxbwZxeYt4A5M/jLRs6aCVj/2kHcb6cxawa/HyQeWQDd7KQQePbDg0Sd2K8x3d8kfcOQ6tch32lI89+/2+9wMcMM61R/iOEiRpssoFUWUJroa1fufGwjn7shs5NacYC7LdxpI+fTWvZUE+dSNQsWVw3s6/XYTAv3Wh12rR67a8Kn3uXcaMRuGTnTTZwlO7nqJJHnbmrBQmxAfjM4ogmarMNG9zGHS4JOljGP7Q2G9WQ9dlLLHNOxPtzHPK1jQc+bPXQcdPNMM+dOM+enNu69dtzbz/MOhr4c4K110++gMLjCeB504jcbsVkLoBN1k8sOOotlB0BgADAtSL5gBcdKUE3cjuLYHaOlQZvdFEwORnq/g+iU+Zszfcwyv8vVrCUHOW3Evjdx58wEAiexZAdQiV0ZIAr1UQl8t2/fYs3y9/aGQMIVF3WrkelQoedrwOHiYKPY92xF8GMbeGQhFq0AeWqHg4EzILCALfZ84ltn8tkm4RVj2AsP+bedXHfiMxbB+yb5Zx7FqCGqK3Pbucrglz0UlEl7hgYWrcSchdSlBFiK+Rc/aD7aJp+wKlZs+EIXWHcRtzvDh2vib45UnjKK2veg37UQUCmcLu0ZXrR7JzVWRR5Qcz9qjDepuGPlLBjMg04C/nw/tBKXjJGDZWFT7VFfNPMfmqlXfOCZ45XsJzbw1Iaf0nGiwTaG72scdGt1kg9MAR76zEEu2cDVBmpYA84bBUf1/A07a81F7686AbLhJu+1E/qsNwsyI7tyMBzdygjyGdexvb00w4YbQFHV2sx8haiuKi+xpqOmofznztANN+ynEHhMSzYvKUmQJY5Wi99Si/mStNgDuaGbbhx6W+1iu6uSZLX1ne+k1DYVGRrytfqcjuqMFTuXDuzHVlydIVCkRWWlRiilCcrMOHlatDwtSq+M0CkiK9UChUy0P493ZxD9ui/ojxHmL57X39ZIPu+I87oxZFxPJu2KEAp4qcnRuRlRstRIpSRGlSGUpscqpAlyiTB9d2xfLfiyl3XFiZ1oY4/WMfILxMUle55YuYijgEqM5w+WhY8aBF+ZY2yaMLkkJlsaW6RIKM9PzsuKj9klvDFEeieB9zL5TT/34/qwaYtQq5FOm3jIoUKsQoKfM4QMaHhN6pBCMZCIYyQpUWJReEoCPz2R16biTjSxrtux30a4twfYQ3vZnzTw+zXUeAWOXKwjz1QRx7Wc90owUxazRBSsTQmulzDb5Sy7mjlUyJrQ42f04IQWP17OHSlnHSllna7AL9QQFwwk4lSgLmWgR4X2ZqMDuehATuBAduBgDjqYy4Doy2G4VahbFdijRruVNHrVjG413eJSBP4L+8L2penDafEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cache hero image&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/10be62f31a62bf19ff647a76a19125e1/2bef9/cache-hero-image.png&quot;
        srcset=&quot;/static/10be62f31a62bf19ff647a76a19125e1/ff46a/cache-hero-image.png 325w,
/static/10be62f31a62bf19ff647a76a19125e1/a6d36/cache-hero-image.png 650w,
/static/10be62f31a62bf19ff647a76a19125e1/2bef9/cache-hero-image.png 1024w&quot;
        sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://handsonarchitects.com/blog/2025/partitioning-of-key-value-data-in-cache-systems/&quot;&gt;previous article&lt;/a&gt;, we introduced cache partitioning and highlighted its advantages for scaling large applications. We also explained how separating partitioning logic from the application itself can be achieved by introducing a dedicated caching proxy layer that handles partitioning transparently. This layer can be deployed either as a sidecar container or a centralized dedicated caching proxy, depending on the system&apos;s scale and complexity. In this article, we’ll evaluate both approaches, outlining their strengths and limitations within the context of modern application architectures.&lt;/p&gt;
&lt;p&gt;To set the stage, we’ll first explore how caching architectures have evolved-from basic client-side libraries to advanced caching layers that offer greater abstraction, scalability, and operational flexibility.&lt;/p&gt;
&lt;h2 id=&quot;client-libraries-for-starting-point&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#client-libraries-for-starting-point&quot; aria-label=&quot;client libraries for starting point permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Client libraries for starting point&lt;/h2&gt;
&lt;p&gt;Caching plays a vital role in modern application architectures by enabling faster data retrieval and enhancing overall performance. As applications grow in complexity, their caching requirements also evolve, giving rise to more advanced caching strategies.
In the early stages of development, caching is typically handled using client libraries that connect directly to systems like Memcached or Redis. These libraries offer a straightforward way to store and retrieve frequently accessed data, reducing latency and boosting read performance.&lt;/p&gt;
&lt;p&gt;In distributed setups, this approach allows stateless services to share cached data across instances, improving both scalability and efficiency. Developers can choose from a wide range of client libraries tailored to specific technologies and languages - for example, &lt;a href=&quot;https://github.com/couchbase/spymemcached&quot;&gt;spymemcached&lt;/a&gt; for Memcached or &lt;a href=&quot;https://github.com/redis/jedis&quot;&gt;jedis&lt;/a&gt; for Redis.&lt;/p&gt;
&lt;p&gt;The diagram below illustrates a simple application deployed with a few replicas, each connecting to a set of cache instances configured in a master-slave (also known as leader-replica) architecture. In this setup, the master (leader) node handles all write operations, while the replica nodes are used for read operations. Each application instance is typically configured with logic or client settings to direct writes to the leader and reads to the replicas.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/b1f077d2f89976795f131bc36052882a/5d180/basic-client-library.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.615384615384606%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAABa0lEQVR42qWRS0tCQRTH/QR9kYig9kF9hqCgokVErUtoEaFgizYhuGgThfSwLHtoi1yk0Kqw7IGhVmZ1VehWSDr3zsx9zLnNvYGFzq7DMDBz5vf/nznHZf0jXIyByYCBKAmwfBZ2xxZidwnn1PzIJUK4FLPA+kK1Xn9/20zH2PoUv+cWzXAo/uBbSd88oR9tro4II7qlYpUSelnKbF0d5d+LYnjEk+gciqydyMyuSk8V6HQIzUWU0ofmyDXKAcZa4FFvsmt4byMpG4YBBkkX8eyuMn+olj+pEK7WNekNIVW34ejpcyCcyb4qjZaYTg2EEFVVLuL1/YCcPVccAWaA5Qnm2ge2OSJoGOeYHbYTQtg3+DjRnV9yv4ClUYzr2PQG832T0cXN299RAYgnWbjGqeNa6Z44xrazf6fQM36wGsuJR2WapqZplFK+M9ABjL9ZBevVGtUMJoZlWS7xkKRKpSJJZb64Tsvv7P59A1eJYEFuNHDqAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A basic client library that works with a single leader replication cache architecture&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/b1f077d2f89976795f131bc36052882a/3c492/basic-client-library.png&quot;
        srcset=&quot;/static/b1f077d2f89976795f131bc36052882a/ff46a/basic-client-library.png 325w,
/static/b1f077d2f89976795f131bc36052882a/a6d36/basic-client-library.png 650w,
/static/b1f077d2f89976795f131bc36052882a/3c492/basic-client-library.png 1300w,
/static/b1f077d2f89976795f131bc36052882a/d544a/basic-client-library.png 1950w,
/static/b1f077d2f89976795f131bc36052882a/d2433/basic-client-library.png 2600w,
/static/b1f077d2f89976795f131bc36052882a/5d180/basic-client-library.png 2757w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/955bd8307fb4466188d33a49a142af82/5d180/basic-client-library-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.615384615384606%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAB6UlEQVR42mNgoDYIDV3FHLrqPzOQwYxFmgmIGf8z/Gds+Loivf7bsgm1nxYHgiT+///PSJJFq0AW/GcAaWKpX9Qt1vFj/eGGT0v/tn9fvwDskFVYHcDAEFR3Jj+g6sQ8KavSUpBrwIIqKuwSHhNFGfw28G44soF31ZRVPPW/lpnU/1we1fR/lTpIibGxMSt2LzddORhUd/G/pF3VPT4ZLSGQmIjfoghez0WveD0X3lYIW6QPUVnPhKSNWQVoKVYDw5quHAisPfdP0qbivoqBjej//wzM0n4LQoAG3uX1WnROI2aRDkQlSpgx2tvbczAw2LMYh86UC60/qxJafoYf4uX68wkB1Wc6ZGzLU+BeBrnAI5cd5KpVq1bxrNq9ij9S9WxErMaZzhiNc65wY1kN9DV8uv+HN135E1B1qhNPlPxn9PBQYdfS0mIzNmZgLUqbKZKid+torMaF/2kGt5cAfcEIjGVmHjUPGzXv7v9hjZeeBNee7yEm2TDAXB2pdsEmRv1MdIz2OX24DI+hpppX56PwxivPAqvPVOFNNvv372eZOXMm17FjxziBbI7//1EiBRGW+esFQlvPiXpMvMWO18Ddu3fLbdq0SWPDhg3qG3dsVFmzZofGmqU7NFatOsaJTT0oGRlLGnMBALRctiB9pMpIAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A basic client library that works with a single leader replication cache architecture&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/955bd8307fb4466188d33a49a142af82/3c492/basic-client-library-dark.png&quot;
        srcset=&quot;/static/955bd8307fb4466188d33a49a142af82/ff46a/basic-client-library-dark.png 325w,
/static/955bd8307fb4466188d33a49a142af82/a6d36/basic-client-library-dark.png 650w,
/static/955bd8307fb4466188d33a49a142af82/3c492/basic-client-library-dark.png 1300w,
/static/955bd8307fb4466188d33a49a142af82/d544a/basic-client-library-dark.png 1950w,
/static/955bd8307fb4466188d33a49a142af82/d2433/basic-client-library-dark.png 2600w,
/static/955bd8307fb4466188d33a49a142af82/5d180/basic-client-library-dark.png 2757w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;In a Kubernetes environment, this basic architecture can be represented as follows:&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/9f3866e3e97912d38507eab8651b9317/71dc1/basic-client-library-k8s.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 72.61538461538461%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsTAAALEwEAmpwYAAACHUlEQVR42l1STWsTQRjuP/DmH/CmVy/1D3gS68WDQg+9WdFgS7EnRRAUCl6EIgVFTS2Ctg1KW7UVaaCa2oMr1H4ktdnNKqbWphsnu7Pz7Ts7mc3Gh2Hn453n3Xee5+1RSskEqg3ZvVVCCLv9P9Rj1ocodsr7G7uNFmZSprdVspAxld/32E6dYSJNijaZC7169Hrr+Pnnfdfff/WYCfMElHElyVsHXX6CruZRYa2lFDOUDnliduPMtblLY59K22GMIwDGyRRhJenyJs7lD4engvkvCKNGhGNL5po8/mL9WN/k6dy7tR0iBWeMpbXBm+FLqWRUn0A+hBAUZd6sjyp+8HRuq7Ds/vkrjC4iARf60tL2ylDh1sir2+W93QiFwA/DUJNTPbVOghuWtHzKKFy4MjV6dPTkkaETDz/kYUsoaTabmiySN8+veOdG3uTulSq/jGDKaAb5IFrynInVZzDchq+0YgwU6Qj2YPpb78DMxZvFz5WYM2pgbZUcK99hPxxOkLYKVARROuTxl+unBmYu3CiuljEhOI5jnKCF4G1k4XF9sHcTxvT9n0rFUWjVtmXXzg4vDI59LLfLlqZDoHCleHF2/06/e7ffXZz8DceCW59TMxKluJnTNmxXLtRBHR/UY4jLbIfZHlQgPxiYbFW2gdtR3CIUp3/qImcBHhonI4vENmkLkl3kLIIgqFarnufB1/f9Wq3muq5xNQuT6B+4XTtCFt95WAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A basic client library that works with a single leader replication cache architecture in K8s&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/9f3866e3e97912d38507eab8651b9317/3c492/basic-client-library-k8s.png&quot;
        srcset=&quot;/static/9f3866e3e97912d38507eab8651b9317/ff46a/basic-client-library-k8s.png 325w,
/static/9f3866e3e97912d38507eab8651b9317/a6d36/basic-client-library-k8s.png 650w,
/static/9f3866e3e97912d38507eab8651b9317/3c492/basic-client-library-k8s.png 1300w,
/static/9f3866e3e97912d38507eab8651b9317/d544a/basic-client-library-k8s.png 1950w,
/static/9f3866e3e97912d38507eab8651b9317/71dc1/basic-client-library-k8s.png 2346w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/07f4abdb93e84f53501c855f5f7282f9/71dc1/basic-client-library-k8s-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 72.61538461538461%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAPoAAAD6AG1e1JrAAADOUlEQVR42nVTbUiTURS++3KbvnPO+QEhrERYLhfRIPADXgyx2YeZdQ1HKqnNUlDa/mimd1k4ysRWgxhTbFtCvBDbRKcjxD8ywqaZTAj83wcElZkRFLd792FT68LlPe+95z7nPOc5BwCyWJYVIoT4IL4wBjwIoYCYPPB38Xb7UTvul7wwDwIgONPpzz2PFktOmmZLco+1HoAQCJKd6WMEAF9ZfVemqHKWKvRjZfIyqwLToElBAIvmhfR7tnfxRt1ABJe3T2FhAfQmMnc4HCICltKExiX0bF/thElW5d5iqjwb2dXugSiIziHaA1jTF+6H/a/Xy1q490xR02x3R4PSZDJlcRzH2Gy2dDQynkH9ss5NtGacdn+Wn/J8zK3xdGNsZ5DNk74NCDkcpXXBsooM1nVc0TWHJZrGKcwBgVZbplCpVJJ4LXlQA1Oob17ecKZKPkID8KxWq8Llcinn52OJUQWihT/Xs1h0cWCt60TXi06x2lCeoKzRaBidTpcKUQwMbT6pHPz99MEg9ty/+c5Z6Op+pLR5POmEiXw7S71eLwZEAKouJgEwKTIBogB8ekeAJdXN1TL6f29r6rHly7Pvt789//lwI9hG3w9zw1K3250Towy5KOXa3pcN0LK6erx9akWsrh+M3UFBQYFenJ+fL9cZjdHC9311Fff/GOuw/Bq7hj6M5dMzRzgsmpycTN1Rw9r+FURU/sS2+TalhY0zAZteTADlELYzceopLALCwyCYVseESuslS8WV6VwmzXp01CcjlFP2iFJ3iwBe9W9KNU2z7iFzmt1uZ9zBYFqQbLN5KMeIgeiK7u31Rs2brUbNylbr0cgdWiYbCiSpHKdc07N4qc6yGqnomF5LUKaZxXTDPKMx1mstuuVWg/rVskEdXrp8JGyO+SUUThaFTAzGiI8xJ6DdbyQ1g1GhcKxliE3UJqCcoI2d2d9cPKfSAYeIThSlvQOQTAKNwCNTIXc6nZkUPN57yY7b/5EIx4RCw9Lkt7vmGfATg57YHtJbpFmZhYUFmc/nk1GbntE7lkVCuqPznTzH/1uBQCB7enpaS1rhoNfr1RJANbX9fv+hUCiU+a83tFdVKlbyB4qLUZ1O3F19AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A basic client library that works with a single leader replication cache architecture in K8s&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/07f4abdb93e84f53501c855f5f7282f9/3c492/basic-client-library-k8s-dark.png&quot;
        srcset=&quot;/static/07f4abdb93e84f53501c855f5f7282f9/ff46a/basic-client-library-k8s-dark.png 325w,
/static/07f4abdb93e84f53501c855f5f7282f9/a6d36/basic-client-library-k8s-dark.png 650w,
/static/07f4abdb93e84f53501c855f5f7282f9/3c492/basic-client-library-k8s-dark.png 1300w,
/static/07f4abdb93e84f53501c855f5f7282f9/d544a/basic-client-library-k8s-dark.png 1950w,
/static/07f4abdb93e84f53501c855f5f7282f9/71dc1/basic-client-library-k8s-dark.png 2346w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;For straightforward applications, using a lightweight caching library with a basic failover mechanism - such as a master-slave setup - can be an effective and practical choice. The application logic communicates with the cache directly, enabling developers to perform basic operations like &lt;code class=&quot;language-text&quot;&gt;get&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;set&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;delete&lt;/code&gt; with minimal overhead - similar to working with in-memory structures such as dictionaries or hash maps. With just a few lines of code, it&apos;s possible to introduce caching that meaningfully boosts performance. For example, the following snippet shows how to store a key-value pair in Memcached using the &lt;code class=&quot;language-text&quot;&gt;spymemcached&lt;/code&gt; client library:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InetSocketAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; addresses &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Arrays&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;asList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InetSocketAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cache.instance1.url&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11211&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;MemcachedClient&lt;/span&gt; client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemcachedClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashSet&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;addresses&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The primary advantage of this approach lies in its simplicity - it allows developers to concentrate on application logic without needing to manage caching intricacies. However, as applications grow in size and complexity, several limitations begin to surface, posing risks to performance and scalability:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tight coupling with cache implementation&lt;/strong&gt;: Application logic becomes closely tied to specific caching clients, making it difficult to scale or switch technologies. Most client libraries offer limited features, lacking support for advanced capabilities like consistent hashing or dynamic scaling. The situation worsens in polyglot environments where different services may require different client libraries, resulting in inconsistent behavior and increased maintenance overhead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No abstraction layer&lt;/strong&gt;: Since the application interacts directly with the caching protocol and data structures, it&apos;s tightly bound to a specific cache backend. Migrating from one caching system (e.g., Memcached) to another (e.g., Redis) often requires significant refactoring.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalability limitations&lt;/strong&gt;: As the number of cache nodes grows, maintaining connections becomes more complex and resource-intensive. Each application instance must manage its own connections, which can lead to contention, increased memory usage, and higher latency - especially at scale. This relation is often referred to as the &quot;N+1 connection problem,&quot; where N is the number of application instances and 1 is the number of cache nodes. As the number of application instances increases, the number of connections to the cache nodes grows linearly, leading to potential performance bottlenecks and increased resource consumption.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rigid topology assumptions&lt;/strong&gt;: Applications are often designed with a fixed cache topology in mind, expecting a certain number of nodes at specific locations. Any change in the cache cluster - like adding or removing nodes - requires updates to the application, reducing flexibility.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fault tolerance challenges&lt;/strong&gt;: Handling cache node failures and retries falls on the application, increasing logic complexity and the risk of inconsistent behavior. This can lead to increased latency and reduced performance, as the application must handle retries and fallbacks in case of cache node failures.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;decoupling-caching-from-application&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#decoupling-caching-from-application&quot; aria-label=&quot;decoupling caching from application permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Decoupling caching from application&lt;/h2&gt;
&lt;p&gt;As applications become more complex and scale across environments, the demand for advanced caching architectures becomes increasingly apparent. This evolution is driven by the need to accommodate dynamic cache topologies, offer a consistent interface for all applications, and shield developers from the intricacies of underlying caching technologies. From an architect’s viewpoint, the goal is to build a resilient and adaptable caching layer capable of responding to shifting workloads and system requirements. From a developer’s standpoint, it’s about reducing complexity by abstracting cache management, allowing the application to focus on its core business logic.&lt;/p&gt;
&lt;p&gt;To address these challenges, a key step is to decouple caching responsibilities from the application logic by introducing an intermediate caching layer that abstracts the complexity of the underlying cache system. This layer - often implemented as a proxy - can run as a sidecar container alongside the application container, offering a transparent caching interface. With this setup, the application interacts with the cache as though it were a single, unified store, regardless of how data is partitioned or distributed across cache nodes. This pattern is commonly known as the ambassador pattern, where the sidecar acts as an intermediary that manages all communication with the cache cluster. It&apos;s a simple yet powerful way to retain compatibility with existing client libraries while gaining greater flexibility and scalability.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/6304409476ae542f4e4893d3bbb6c1ad/56d77/sidecar-caching-proxy.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 77.23076923076924%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsTAAALEwEAmpwYAAACZUlEQVR42l1Tz2sTQRTOPyFePXkQiyc9pOChdwUp1dCDFAJBqV5apdUqBcWLB1E8GagHD2pbyC/TWhIKaRo1VUQwTaptkt24MSaNabK/d3ZnZn27k6aJb9nJe2/mm/e9b188tm1TSu0joyyklPby/f5h5IQe9tPsaLE0F03zXF3HhK59+sXXJMgTQntrqYE/7qJvvIksajMwdjdWMpUh35LXH41/kZodNORbDIYLkDeQiRwzdU2dWxIDL+WJoFhqWFAfcF3wu03+0u21yceZ2Fa7JLQuTK2+iu9QbCiqpus6vDbFCxvqrdft+ZBU72DWaxcc3+RPX1k8OxGOZMVm2zh1+e2LUB7ypoX7e8bmQNjt+W9bA+YrHyp8XYPrElmB/yM7pwnBGANFZJkPEs8Di3fvrT5RDM2mzuP5T0wTGYSQXpK4YAj3xeaJmTOewLHjUyeFg9+OipR6mJKf842L0+9HZxKpnKToeGw2EdngIG9BaThBqYWt5Vz8UerZm1wEYZP2qw1HzwciY3eS0WxHaMhef3ghCmpT3XCIuFyo2rRr38kBZxML7iN94BQ3cj02OpuMbnX4WsfrDwVD29CELCuqqimKaiDt4XjxxvCPa+cKfEFz+iJ2l/bXnX3fXHL8/np6W1INfHV+PZ6pMMEczhaMBX46Wbk5/HN6ZFfY1bvgnjamRZCJoRTGTOCjMWQFpAOrnJeaVUSALaPt7pHePMuybCDE/P68uxJVl0F+x3Ov89iD/wnr0AAJKz408GFKNU1nMGYDYDjUarVEURQEYW9vr1wuF4tFWDmOq1arbIt9dpcX/QfE6SSAkC3SYAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A sidecar caching proxy that abstracts away the complexity of the caching topology (with partitioning)&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/6304409476ae542f4e4893d3bbb6c1ad/3c492/sidecar-caching-proxy.png&quot;
        srcset=&quot;/static/6304409476ae542f4e4893d3bbb6c1ad/ff46a/sidecar-caching-proxy.png 325w,
/static/6304409476ae542f4e4893d3bbb6c1ad/a6d36/sidecar-caching-proxy.png 650w,
/static/6304409476ae542f4e4893d3bbb6c1ad/3c492/sidecar-caching-proxy.png 1300w,
/static/6304409476ae542f4e4893d3bbb6c1ad/d544a/sidecar-caching-proxy.png 1950w,
/static/6304409476ae542f4e4893d3bbb6c1ad/56d77/sidecar-caching-proxy.png 2275w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/9ffc33dbb11217d4134bb94c47af57e0/56d77/sidecar-caching-proxy-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 77.23076923076924%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAPoAAAD6AG1e1JrAAADgElEQVR42mVUWUwTQRie0oPSlBaLGuq1kVSORcJR463rg1GUskLLVqVgFbViTRPxilBgKhj1wSNEXhqqROFpNcgDRxWTJZ5AlwaKGH3ReOuD8cmgAV1ntl3EOMmff2b2/775zwUALYqiFAzDyEFsCQDI8B3eQwjjzGazEm3jJFuSJFXYRrLHd5K9CMC6sPr+wuLawUq65tFB4/pjxYtWMwk2b2h38amBzCgIigCSYVRYa7e1rdMWXNuv33rdOo9q0TIMiDpEQU40tMGRcnvT8590zfCULrfqbZrlYoa9cWLK1hCujz7s17Asm+CGUNveDHWGks5Q4raOaV3RrWlDMbsy6p0Q95fQyzut9fyLgqP9vCH/8GvLkcAaa13otc37oBY6gA7CS4ZAIJBYXl41n4NAoS/quDGn6OYnnaV9PHlLW0YsWbIZQrsv4th59sXPHd7Qb33+kXdZ2y9kIo9/2XwR0cPs7MI5SMkJglDH8iWzWFwa/A3n2OxyKf/JIX3y0QIrHCujawf3LN54jDYVeOJL4FgpXTecHgNpiJycJIqg1C6/SwO/dzT6vre3NU52nPf0NuvwY1KRxIq6pBew44Iwc8bVx4I9IylSawRAc+Dm8aWXp4JvLglB4cr0vS9NH28TMZwMAViR2ep9stlWH35Jn37yKinvUHDJhlpjacPoqNUb2o+/p6+lE1NTU/Umkyne0+yJh8I1e5Nwva5RCOxyclBNgVjbSDlkfM+cjG/8c+GJgbdJ+Yffb6wI5DG+sW8ldUM4h8jLffOQpyoChYwaVbYV8MZdCn4VDYbTKcKpnunD2YSlZyIfCk8+eJdkrvqw5VDrSsYX+WZvGIYCBHHnzl1N9vtZvdvtTmlud+gqcyaeOsjw5N6s8cmK5SMrxHoA1DZSyDu8TzeV+sZCdM1gxLDCfYegYAoK+bHVO1IhFsXlV+IcVVdXJ7ACkDvJ8F1HBv+jPCP8tYwcyp0hlIqCpcDTG++EnLq1lTX4/X4lJkFdoJAKI04KKU6KjCG5lAPrHq7enTmwjGGgKjae0SrjQ3SegZyigAKRzUVEWhGIiGbPOrbFe5ZFwl/QQw6KuNmEIqkkuC/RiKkk4ThOwfO8EnuMQpZLZzyGLS2slo2mTML/vzCgu7s7pb+/P7mnpyctGAzmIJ3d1dWVh3VfX18WujMhbezs7EzG9rP+OOo/DM94iCG1XA8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A sidecar caching proxy that abstracts away the complexity of the caching topology (with partitioning)&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/9ffc33dbb11217d4134bb94c47af57e0/3c492/sidecar-caching-proxy-dark.png&quot;
        srcset=&quot;/static/9ffc33dbb11217d4134bb94c47af57e0/ff46a/sidecar-caching-proxy-dark.png 325w,
/static/9ffc33dbb11217d4134bb94c47af57e0/a6d36/sidecar-caching-proxy-dark.png 650w,
/static/9ffc33dbb11217d4134bb94c47af57e0/3c492/sidecar-caching-proxy-dark.png 1300w,
/static/9ffc33dbb11217d4134bb94c47af57e0/d544a/sidecar-caching-proxy-dark.png 1950w,
/static/9ffc33dbb11217d4134bb94c47af57e0/56d77/sidecar-caching-proxy-dark.png 2275w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The diagram above demonstrates how sidecar containers integrate into the overall architecture. These containers are deployed within the same POD as the main application container, allowing them to share the network namespace and communicate over &lt;code class=&quot;language-text&quot;&gt;localhost&lt;/code&gt;. Typically, adopting this model requires minimal changes to the application itself, since existing caching client libraries can often be reused. The sidecar container takes responsibility for managing cache operations - such as partitioning, scaling, and fault tolerance - freeing the application to concentrate solely on its core functionality.&lt;/p&gt;
&lt;p&gt;That said, while the sidecar pattern offers a cleaner separation of concerns compared to directly embedding cache logic in application code, it does have some drawbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Connection management&lt;/strong&gt;: Because each sidecar container maintains its own connections to backend cache services, scaling out the application also increases the number of cache connections. This can overwhelm cache servers and degrade performance. Unlike centralized approaches, the sidecar pattern doesn’t easily allow multiplexing of multiple client requests through a shared connection pool.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Configuration complexity&lt;/strong&gt;: Introducing a sidecar involves additional configuration in the deployment setup. For instance, when using Memcached with Ketama consistent hashing, you must configure the sidecar with the appropriate hashing algorithm and backend cache node details. As the system scales, maintaining accurate configurations becomes more challenging and error-prone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Operational overhead&lt;/strong&gt;: Since sidecars are co-located with application containers, they must be monitored and maintained as part of the overall system. This includes tracking their health, managing restarts, and ensuring they run correctly - adding to the system’s operational complexity.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even with these challenges, the sidecar pattern represents a valuable step toward decoupling cache responsibilities from the main application. It’s a practical and scalable solution for most medium to large-scale systems. However, for larger systems with many application replicas, this architecture may become inefficient due to increased connection overhead and higher maintenance complexity.&lt;/p&gt;
&lt;h2 id=&quot;dedicated-caching-proxy-layer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dedicated-caching-proxy-layer&quot; aria-label=&quot;dedicated caching proxy layer permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dedicated caching proxy layer&lt;/h2&gt;
&lt;p&gt;For high-traffic, large-scale applications with complex caching needs, the sidecar pattern may fall short. In these scenarios, a dedicated caching proxy layer - managed independently of the application - is often a better fit. This layer acts as a centralized service that exposes a unified interface for accessing multiple cache instances, enabling more efficient use of resources, improved performance, and simplified management of cache clusters.&lt;/p&gt;
&lt;p&gt;Such a service can optimize connection handling by multiplexing multiple client requests through a limited set of backend connections, reducing load on cache servers. It also supports advanced capabilities like consistent hashing and can adapt to changes in cache topology without requiring any modifications to the application itself.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/c9df4cf8cc8ac1e3bd54e6676161f3c1/dec58/dedicated-caching-proxy.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 87.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAIAAADUsmlHAAAACXBIWXMAAAsTAAALEwEAmpwYAAACnUlEQVR42m1TzU8TURDvzT/B/8F44ObJIFfRkGhiEC9eBDwYNIAxWrkQPxITbyQmJmhMNEo8FOQbNYIlAU0oFAK0BcpqS7vUbne723379n3UeW+X7UqcvJ1kd+c385vfzIvU63XOef3IeMiC1yAGPGOcMs7kawQel7CN3XJyp2wh6sUfRQsH0eF04TICvJc3mjtjF/vmNnMkDD7GCBzUjKfxdBLnNOpXTin6+Z7xjoH5RBYR1/EgUBB8WtEnF5WV7RKUpwRXTPfWG6t9yBhbcXxwvmR1RD9fH1zYUyl2EGWCKRVgduf596ZrHy/fndYNi7iYUL6YciYTdkFnQCQS9EMIAZBt24CFjIRQRvHofPb+0PLL2KYLwsB3/o+uovIfHQ28WB4cTuTLlDMMRB69SmiG7boY/lIqhAAkpcCGpw+VjcIOkr8iXmMt3aOX7n1Z3cec2EtrSkv3WDqrSqTgL4+gk8xvn3x45kT/qZHEhA/O/Nabu2KtvbNJxYXBraWKLTc/ZXNl6DnQ2+OsmuX+0aedH6LrBykfXENkbD4L7VVqDNSuVNF4XLFqDsibzJTfz2biqwce7Ua7db9nsTSGhTUDgWQIIW+8Um3S82zhdPtIW++UXrW4ZH58SfYL1XNdsQu9c1t5gh3bX0A555mlX09er7ybyTgOxi7SVOft48JwNK9s2Q3BWm9PXB34ltizGXW9PhtbLQp6udjuutFzNnWjaevHlOGDc6rZ1jd15cHX9AEJ34G635x3xHoyyneT1vZPE/gJsFfBrOGKYYs5ynsQruztTGjVuTyyZ18eUNZBgRKu6wYpAgk9j5AD/TcECwyGYZqmZVmaplWk6dKq1Sp8h4z/UTswYIiklUqlYrGoquqhNEgH5fw5N4L5X2723G1/ILsNAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A dedicated caching proxy that abstracts away the complexity of the caching topology (with partitioning)&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/c9df4cf8cc8ac1e3bd54e6676161f3c1/3c492/dedicated-caching-proxy.png&quot;
        srcset=&quot;/static/c9df4cf8cc8ac1e3bd54e6676161f3c1/ff46a/dedicated-caching-proxy.png 325w,
/static/c9df4cf8cc8ac1e3bd54e6676161f3c1/a6d36/dedicated-caching-proxy.png 650w,
/static/c9df4cf8cc8ac1e3bd54e6676161f3c1/3c492/dedicated-caching-proxy.png 1300w,
/static/c9df4cf8cc8ac1e3bd54e6676161f3c1/d544a/dedicated-caching-proxy.png 1950w,
/static/c9df4cf8cc8ac1e3bd54e6676161f3c1/dec58/dedicated-caching-proxy.png 2133w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/66f553e5e5f0fb5f762b1c62db88ec41/dec58/dedicated-caching-proxy-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 87.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAD6klEQVR42mNgYGBgDA0NZWZAAEYgZgJhkHh9fT2cjSwOo42NZ7Jqhdaz2dvbs0DlgQYW9nKGVp02c8/e7iBhmCRfX8/ABFXACKVZGIAGh65aBbYAJAaTR3MIxGGBxft1whovPXfO3vafTS1sFkjM2NiYFUSDXAHVDAcgfihQs4pHLju/1/xkPq+FFfyuswzgCsKazumHN1+97pCx6QuPTsKi////MzJADTE2TgMb7F910iCw6licf/lhB6AlbKt6Qzm1QifI8fmsfsEfsOm/oN+6CriBASV7lcOaLh9zzt76mEMtvPb//ytsocBwqa//zwQKn/9A78S031oWXHf+f1jT1fMpZR0ypUl+vBYWhZxCPgsShQIWVQq6z9IGG4YcJqtWATUDDWlvbxdMS4O4LD6+nmP/fHuOkLpTiYHVJ1cFVJ+qs7BwE1JREeJDCjsGpMhiYPDIXSsT1nB+LtCFm9jVIxNBYuFAVwfWnp8WX79Foru7mBskNnPmTFaQa+Xt7Tnk5OQEQZFUfGuuVsnN+QahU+p5YLHMEFF/SS+i5cZ9p+xtPzm14oBhyMCU0bLHM6Th8rOCzj1GkIjYz2Jfv58FZIh9vD0HUIir9M4Mk8bvy94D8f+aDwuj4GEYXHlcL7zp6hOX3J3/OTSi54HE4ur32gXXXnyZ1rRb93891GYo8PDwYBdkYODPPtspVfd+8eTa94sWlT+ZZohIBvHzBcLqL8R55G8rALrQ7P//erbA4p1iwTXnYzIrlgqu6rXg9C87ahVcdTIvoOaoj5aWFo++vrwASO9/hv+MIIwchkxaWqFsHrlzRb3TF0jrucZwd3R08MPSHjiW7RlYYrvuLQtpvPI/ouX6haikanljY0kuBlSASNh+xXu0QhsuPXPO2Q5M2OEzgOkQnJ1CV/1nhqXDkJpTEUE1J2cE1pwozM2t5yvt9ONNstwrH6N5ana05unlkVoHzeBGB9ac0wemr+uOmZs/8WrHLYFmBxZYToHlmlWrwF5iBPkIxM+1P2ERp3nhY7zm5f+xWmcQkRJUuks1vPHyZaALPwGTTSdyDoGGCxCDwgmIgbkILBbKwByqVc8Wq3fYJt7wqHOo8Sp+WJoEJ+yoii2Cya0bxeXt4znSgC6CBTA430LZwEzAgit/Q3IJMDXAiiJQLpnTWcoLK75AGoAGcCAXVUA+H7Q4gxnIWF8/ka+ztJMXJadADWGE5YbZs2cLLV26VHDx4sWSQLb4qlWrJJYtWyYOYi9cuFAYJDd//nwONL2M8JyCDICamXfu3MkNwmvXrpUB8hXXr1+vsGTJEnkgWw7IFgBayrV//350LzMCI48LALjhiCmK3g/NAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A dedicated caching proxy that abstracts away the complexity of the caching topology (with partitioning)&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/66f553e5e5f0fb5f762b1c62db88ec41/3c492/dedicated-caching-proxy-dark.png&quot;
        srcset=&quot;/static/66f553e5e5f0fb5f762b1c62db88ec41/ff46a/dedicated-caching-proxy-dark.png 325w,
/static/66f553e5e5f0fb5f762b1c62db88ec41/a6d36/dedicated-caching-proxy-dark.png 650w,
/static/66f553e5e5f0fb5f762b1c62db88ec41/3c492/dedicated-caching-proxy-dark.png 1300w,
/static/66f553e5e5f0fb5f762b1c62db88ec41/d544a/dedicated-caching-proxy-dark.png 1950w,
/static/66f553e5e5f0fb5f762b1c62db88ec41/dec58/dedicated-caching-proxy-dark.png 2133w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Along with the added flexibility and scalability, a dedicated caching layer also introduces certain trade-offs. A primary concern is the increased architectural complexity - adding a separate caching service means introducing another component that requires monitoring, maintenance, and management, which can raise operational overhead and introduce new failure points. Despite this added complexity, the trade-off is often worthwhile due to the advantages it brings: centralized cache management, enhanced performance, and more efficient use of resources. One key benefit is that the caching layer can be scaled independently of the application, enabling more precise resource tuning and performance optimization.&lt;/p&gt;
&lt;p&gt;Another important factor to consider is the potential for increased latency, since the application must make an additional network call to reach the external caching proxy layer. While this latency can be reduced by deploying the caching service close to the application - such as within the same Kubernetes cluster - it still doesn’t offer the ultra-low latency of &lt;code class=&quot;language-text&quot;&gt;localhost&lt;/code&gt; communication available in the sidecar model.&lt;/p&gt;
&lt;p&gt;Ultimately, deciding between the sidecar pattern and a dedicated caching proxy layer depends on the specific characteristics of the application, including its traffic load, architectural complexity, and performance expectations. For smaller or less complex systems, the sidecar approach is typically sufficient. However, as the application scales and caching demands become more sophisticated, a dedicated caching proxy layer tends to offer a more resilient and scalable solution.&lt;/p&gt;
&lt;p&gt;A practical strategy is to start with the sidecar pattern and evolve toward a dedicated caching proxy layer when the system outgrows the initial setup. Tools like Twemproxy can help make this transition smooth, allowing you to adopt the new architecture without significant changes to the application’s code or configuration.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;In this article, we explored the evolution of caching implementation strategies - starting from a basic in-app caching library, moving to the sidecar pattern, and ultimately reaching a dedicated caching proxy layer. Along the way, we discussed the core benefits and trade-offs associated with each approach, helping to clarify when and why each strategy might be appropriate depending on the application&apos;s scale and complexity.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://handsonarchitects.com/blog/2025/partitioning-of-key-value-data-in-cache-systems-part-3/&quot;&gt;final article&lt;/a&gt; of this series, we’ll shift from theory to practice by demonstrating a sidecar pattern implementation using Twemproxy. We’ll provide a working Memcached example that can be easily deployed in Kubernetes, along with configuration details to enable Ketama consistent hashing. This will allow you to verify how cache data is distributed across multiple nodes - giving you a clear, hands-on understanding of how the sidecar pattern works in a real-world setup.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Partitioning of Key-Value Data in Cache Systems (Part 1)]]></title><description><![CDATA[This is the first post in a blog series exploring cache partitioning strategies for distributed systems. It covers how to handle large data…]]></description><link>https://handsonarchitects.com/blog/2025/partitioning-of-key-value-data-in-cache-systems/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2025/partitioning-of-key-value-data-in-cache-systems/</guid><pubDate>Wed, 18 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/6b1556c45fe29f82ccd5553096e4ee13/2bef9/cache-hero-image.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEc0lEQVR42mP42aoHQm1ICFMEUxasgAHC+tGKIv0DST+Q/R2qQP9Pu/7fDv0/bXog1KrLABT93QZCcFOBKn636cNF/rbp/m/T+d+u+6dN93Oj9odG3U8tBl87jN+1GTMAFb2o1XlaowM0/lebPtCsZzU6j2u03zXofmsB2XCpxvhwjc3BKsudFVZbKmxXF9vMybNpTrZcXO/DcKdC62qpJtC8B1Van5t0vzbrPa8F6Xxeq/O5Gegi3a11ztlhppYhHmYRvvqRgZoxYbpZyWKefjUZ3iDNDyq0vjTrfWzWe9Ok+7vLAOgWoMd+tOu/atD73224o9Q8O9RQJCtPLidLoaxMNjtbo7tDLKu0viiK4WWdDtCHp/LUokyE7dT5Cp0l/3Xo36/UWhGvODNM7lqZ1s5K2+ZMB4/GKr/GCp+GCu+a4oDmCseU+ElV4QxvGnSP56pZaAi7GUprK4mkWIm8atCZGS7f4SvVH64G1L+hwj7JVzsiSjc6Ti8qRjc2UT8xRd/VQ7G7wIUB6LByRzEDFdFzEyIPdoSuLnfbnKrc5y+5rtRpV0/8vOqgVbmWpbGyFi1atjUa9k1aFuVqXlMMtIsVygttGH606K5M1gqxVji+tHbHlLyt7ZFnirWafWQWVYecXlyxr959Xo5NV4FBeKtJZI9F9DSrkG7T+PnWtpUatSUODG9qte41mFWG6E1MNM/y0s5zln1SrbWp0PxwncvCXKf9FbbzCu0KgvUb4s2q44xa0s2bsy1bC+3yksxm1PgwfGvWPZajVh6oLSHCJSjIqS7D+7xK42S+xpR8j6W59nMzLNYWmk2N0WqMNJzjLr3FV2aZm9TBQPnZ5kKLMqwZtmdp29sa1sbbF3up84sIpjkrXqk2SijNDS7My0rx88gvyMoIXJxmuCrTdkOA8tFI1QMRSo9S1Pa4im7KsWbwczFydjT1cDELdDf197YJdNROTAsJbGgNLCnyzs2LrK7yTE/f3eS1v8tmTZ3FygrbJfkOW0qcZiSZL6n2ZQjwtna1M/B1M/fxsPJxt3BzNvH2dQwJdg0LcQsLcLAPDXIKDZ5fZruzR/78XPWjU1UMNXn5RPglpQRaMp0YnO0MjE203F0svJxNPZzNgnxsAz0sgr2tAjwtA9zM0rMi55V4zc0129enemGO5u2Fakn2Ig7qgmIcDJXRZgy25tqL8hw3VrpvrXKN8Db397QO8rbJS/EJ8LGP9DLdXmy7KN18RbXq711a/4/pLK+S6UmxWN/oPafQfmdfGEOFv05fvHlltHWKn7GXq7mbq5Wzo5m9jaGVhV5tsN6OAvOteeZbCk3XFesf6tRZkKm1o8B6W4ntljLHjVWuDOeqbTflmLWG6sW76YU6aCW7ahR7a3WH6y1JMTpYanWsyu5gmc3+EuvteZYrU01WpxstSNDpC1Gr9ZQvdJJmOFJuc7zC5myN7alK64MllmeqbE5X227LM1+XZbIoSX92nPa0SI3uIJUqT/k0Oyl/AxEXLSELZQFdOT5TJX4AH4LwpwmIqEoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cache hero image&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/6b1556c45fe29f82ccd5553096e4ee13/2bef9/cache-hero-image.png&quot;
        srcset=&quot;/static/6b1556c45fe29f82ccd5553096e4ee13/ff46a/cache-hero-image.png 325w,
/static/6b1556c45fe29f82ccd5553096e4ee13/a6d36/cache-hero-image.png 650w,
/static/6b1556c45fe29f82ccd5553096e4ee13/2bef9/cache-hero-image.png 1024w&quot;
        sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This is the first post in a blog series exploring cache partitioning strategies for distributed systems. It covers how to handle large data volumes and high traffic efficiently by dividing cache responsibilities across nodes. The post highlights why partitioning matters, compares it with replication, and explains core techniques like consistent hashing for load balancing. In upcoming articles (&lt;a href=&quot;https://handsonarchitects.com/blog/2025/partitioning-of-key-value-data-in-cache-systems-part-2/&quot;&gt;Part 2&lt;/a&gt;, &lt;a href=&quot;https://handsonarchitects.com/blog/2025/partitioning-of-key-value-data-in-cache-systems-part-3/&quot;&gt;Part 3&lt;/a&gt;), we’ll look at how to decouple the caching proxy from application logic and conclude with a hands-on example of using the sidecar pattern with Twemproxy.&lt;/p&gt;
&lt;h2 id=&quot;when-to-use-cache-partitioning&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#when-to-use-cache-partitioning&quot; aria-label=&quot;when to use cache partitioning permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;When to use cache partitioning&lt;/h2&gt;
&lt;p&gt;Replicating cache data is a widely used approach to achieve high availability and fault tolerance. It involves storing identical data across multiple cache nodes (replicas), which improves read performance and ensures redundancy in case a node fails. However, for applications with large datasets or high traffic, replication alone may not be enough. That’s where cache partitioning becomes essential.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/9e8cab22f56e2531d8281f6d7c80973b/89819/cache-replication.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 96.6153846153846%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAIAAAAf7rriAAAACXBIWXMAAAsTAAALEwEAmpwYAAACoklEQVR42o2TyU8UQRTG5+/x5kGNFw8mnrx40sRo2CQoCIJLjFFDIq6JMRJjoizKzqAZhAGXEaLREGAiopHAwSDI4sx0unuW2rq6uqvG1z0DOoJiZbpS/c371av3+qtAtnAof8ZUxA2qmSxhMCNtKZXddAT+hP24C/ejO4tDeyvDe8oH9te+WE5gEOWGPQIbSaXU4Uuj+6qGDl0cOXD21Y6i0My86cHy3/AaX3H9/baDwd0l/duPPN1V0j+/ktkCVj6XO9vSSmpyenVkShv/koC0rlT/VTPsnxUia3NYWiJfpSv/3jCIZ1xYhDmY0jRi2CJcUa7SRDDuEktykQUFFrbjCpl1ZP6r5DOTlMFw2nVdXdeF4+TKMAydc2u9Ilibhq5+67kHL2jOu8947KszOiui3zxyQXffzonXM/abOfFx0VOWDE+JgDJrTy06uUI8uGGAFD3M3Bwi1R2orCWjZeSdl7SsBV0L03NBfKwVgdIYoaXN6OogPR/EJU1oxfToADSyYYBWtqErz0hdNz7RhpZNtzHCzvTg+hC5HCI1nRhC742w2i5Pgd/xx+i77ubhG2F6sh0Bebob13Tg1aQLmSseIXit68IQGkvJuxFW3urH9OCyZgRV5OH6fgJ/VHfiqnYMC4BvDXuHBKW6A5c2Z36k5O3ntLgpA6c41YmPPsjkM8MzOS96J3jfOH0S5cOfbNjuw4LTF3V6x1DfJA9Pc2gPtC04YYMCMYPTHCxQYBIbFNtac5u0GMYopeQvfziCE5RyHbvQJMqzNFiXUMYsLoSwLK4bpplMOq6r1u6pYZiJhEYIXb9CgUJrSiA559JPCH6Q/gDzgAjzVt72t7BtmzFGCIHZsizAcsZShVc6sKnjgdE0LR6Px2KxdDq9McA/lPoJUwAEslbc6o0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cache Replication diagram&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/9e8cab22f56e2531d8281f6d7c80973b/3c492/cache-replication.png&quot;
        srcset=&quot;/static/9e8cab22f56e2531d8281f6d7c80973b/ff46a/cache-replication.png 325w,
/static/9e8cab22f56e2531d8281f6d7c80973b/a6d36/cache-replication.png 650w,
/static/9e8cab22f56e2531d8281f6d7c80973b/3c492/cache-replication.png 1300w,
/static/9e8cab22f56e2531d8281f6d7c80973b/d544a/cache-replication.png 1950w,
/static/9e8cab22f56e2531d8281f6d7c80973b/89819/cache-replication.png 2035w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/1c0100feb1ccc0d4f3f936fd5f09c461/89819/cache-replication-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 96.6153846153846%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAAPoAAAD6AG1e1JrAAADdklEQVR42pVUb2hTVxR/tam2JvfeYKuzNtVN9IvaoqvINmT5MLXv5SVWk74ptFA/FVRUpm66zfbNqaAg+C9fHtQm77000aslJYVYzEq+FY1pa7FSUXEwEURndQ67oujduU2a2Q2nuXC49/7OPb9z7rn3HEH4n1GnJu3KvvR85Ye+irpDqUpFvTpXEFiBkP/IGHlb0hFvy8C4r3Xwoa91YNTbOnBPUfsXcZ2qsml5kfGh/DTc5z3Q/3DDj9dueA+k7vrUobHNh0dW5kkoFGRFgIh++frnm8zXMvDC1zL4GvZjG9XUsg8lLMgJy0S5ZTutatqTkFfvTDZ69l8VFXVwlVNNWv7t+L1DFQQLFYSSU4KAJ4yW75gNCSicUNZoRe9ncKoWxZ+07XY3l33/ybKPVLHB4axqcJQ7tTLSFLXXNGjlNc2UzPtKLy39og0hmOdtOFOqfENLBIVOF5onnEyN9vbZI7NHE0eJBspINFpJh4enT+qikfbK3/rAmD9ZNh2xWGxmdzetmNxPGXNq2z6f/+Xx75ArtAVJ5i7r2mAjx7EcXIVd5jbi0r/FkrGdyMF6jhO3+SmWja3IZezFUghws15Q1FwABdh97gpxU0Zq29PI1fGUeOg4ktsXY/lcnMhhhmoD17FkPsBy5CVaw/FIDARwfQhJxn3sPs+sYrg6+9loIUTWj13GOJb0YTD8HbtCf9k94WqILo5F/THgt5Ck34GI/rBKoSrQx4DoKeAjoB+B/Usid9T8QygZKbjSGJA95oTgYMIQ5suwfw3EjyAVT7ihtTa4FEmhS7Bm2fOPMNyCiOEVWUKlkKy/kIIDb8DoT8SJPfSF1RNeSjwXEtgNVxMNwM3n2E1f2US6hHguxuEMswEG+DOyvpMBx4rcoyDRbESi7i+V9VNYDILorTxySPim4rXB03PqzDOAnQRnB/kXIWKw3rZO98+tC2VwUT80SzTxf6v412QxYzT3WvxLMKbNZKMJwpg6pdQYSxexN1cwY7nKebs04DCIQmnhiUDAfqytDcXj8Rl+P7WZZqfD7Ows17R00dvlFqTdFR0dFxcGAlF7pqG8o62pQMzJ+MeFtWUyUgrOuCSTSQvX8TmvjsiJgaCkq6sLJRIJYpomNgzDqmla0WR1ZFKSR8OFKGw9PT2Vvb29C2D9MUjZO9uB02lxOD4r+RsHNpMKSBsH8gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cache Replication diagram&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/1c0100feb1ccc0d4f3f936fd5f09c461/3c492/cache-replication-dark.png&quot;
        srcset=&quot;/static/1c0100feb1ccc0d4f3f936fd5f09c461/ff46a/cache-replication-dark.png 325w,
/static/1c0100feb1ccc0d4f3f936fd5f09c461/a6d36/cache-replication-dark.png 650w,
/static/1c0100feb1ccc0d4f3f936fd5f09c461/3c492/cache-replication-dark.png 1300w,
/static/1c0100feb1ccc0d4f3f936fd5f09c461/d544a/cache-replication-dark.png 1950w,
/static/1c0100feb1ccc0d4f3f936fd5f09c461/89819/cache-replication-dark.png 2035w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Cache partitioning involves breaking the cached data into smaller, more manageable segments (partitions) and distributing them across several cache nodes (shards). Each shard holds only a portion of the entire dataset, enabling the system to scale both read and write operations across multiple instances. Since each shard uses its own memory, disk, and CPU resources, this leads to more efficient resource usage and improved performance. The primary goal of cache partitioning is to enhance scalability.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/ff1b4071eb90a8a4ef75c3e0bd46b30c/e4d4a/cache-partitioning.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 82.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAIAAABSJhvpAAAACXBIWXMAAAsTAAALEwEAmpwYAAACyUlEQVR42o1TbUhTYRTeryDon2CEhNSfokAoKS1BS5EoMwnDMMWKaJLhF6tZbomJSUQmFZYESWY5Z1qUJFgKClESNFKzue2abbt3brvb3b139/uz986hhhYeDpf3Pfd5zjnv+z5Hp65lirK8WFqvNt3KjSxrQNssesY8UnJjpLRh9FTdcOc7u/ZrrRx/kSVJQ/QOu3YVWfNqh47VvN9T2q9vGY+1sD6y9SOUlNe9/9xAatnA1vwX+lvj66q8mB4OUO2W7209U496J+912cZtC+uqHL8iUVAlUZYkkRdURZQEUZRV5V8XJooAJqiCwOM4i5NMlKcYnsNxghI4XpQYliUZguJImpdkhWVZWZaXySRJ+BAvqBZYQIIoCiLhUMjvW4iEI0F/ADQsS6IX9qBhlOM5LzzPskycDPohsGgQJQhO9Uc4URBABA0gSBB2+dyQD2Y5DUpE6IgnjAUZHxzVoLHz6cZmhaouovktDbyii5j2qF/cE0kNGXvvnshqL0kwpTWN3mdDqrkAMuW57px3G3KgjqtIvHL3J7bkMXF7kG56QxU+CH+cVi3fLDp9YlpbYebDYl3ltpPPL/gdyunkKVPBXKveXZ5qrzvq4hjt2DrrBHvxKWHoiQIv68DGfqqvJl9vuLI7qTFjS8OBTcaUUmu136lUpNkN2c6aLEflwdnm4vk4+eVntryTNPZGr/VRZ59goz9Ui8260ZiyvTk7+WZmQv2+Ystln10GZOMRlyHHWXvI2VT0i6Vj5KFJvqqbvN5HmfupS8/wr5D6wTGSaE7f2ZK7oyV3szm9erCRRNSaww5TPlR/HAL8Vr1HiT2WTpAUGJORiIzEvpIMlCi70N8zfpc9ODcTgEiOBjgU5mEn55vjvE6WxMSYnFYrDARlhcBwHMMjIQwPRziO/99ILg7t0ugCVdA0DXRFMzRFUwIQn5ZxhWtIzf4AlpZbwCt6lYwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cache Partitioning diagram&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/ff1b4071eb90a8a4ef75c3e0bd46b30c/3c492/cache-partitioning.png&quot;
        srcset=&quot;/static/ff1b4071eb90a8a4ef75c3e0bd46b30c/ff46a/cache-partitioning.png 325w,
/static/ff1b4071eb90a8a4ef75c3e0bd46b30c/a6d36/cache-partitioning.png 650w,
/static/ff1b4071eb90a8a4ef75c3e0bd46b30c/3c492/cache-partitioning.png 1300w,
/static/ff1b4071eb90a8a4ef75c3e0bd46b30c/d544a/cache-partitioning.png 1950w,
/static/ff1b4071eb90a8a4ef75c3e0bd46b30c/e4d4a/cache-partitioning.png 2198w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/5ba8ec33eb29270e9ef16f25a189ca75/e4d4a/cache-partitioning-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 82.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAPoAAAD6AG1e1JrAAADoUlEQVR42pWUbUxbVRjHDx0ItLSAbuwNZINKb28duGDMYkJY/LJSVrqV3A3Xl7m5sIRvGseGa3s6hk62JW6afWhGUrhv6IUJ1ljX+NLEslpmXy6IUwQNLNuMC07TdrIsLsdzCyHsgwonee7/5sk5v/N/zr3PAeA/B8rCj4VAmffVDwiRTNJm51id2RGLmU/GolhHzc7YhNkR7ViYA2UrBtbDYLak+7smW6lT3yOzM/4LBs42u8S/qdM/fLq46+qB+7qmjljO3UKUezzV7B6fP9B9E7V0TX6yaoeAotZIYj0pbt13fOSc6UTkAtUeOtvcHrpkPB4xLXO48jNFeHIQgDwrAIrdG2vlpvL6IpzL3qHa8SSghCf+F+bxeHK8wWAeBEDO1NVthC/oS+sq95eZYLCINxrXb2/q3vSaEM4/U/h0sUWtLzW0eTe89Orl9bWtnpxAIKAQBGHNY8BQKFQcvCqoJRefD3IVbA9bLuW/EITNH9OCmu/hN33mHdoinV0QwmxhmCH7r/aX+aI+efAaRwQCtOIxoP3F1srDh7s14GXf2m0WRjMhZMoC7799pMrz5dnKztEBXRvrfc4X9cilct/q+HpdZ0277s3G8GYLNUxSB4UNi6gsUGDgjhbq+1LKXb2isoFOqAzsfVDD7O1FQi1MMvdgmrnhSLERV5pPdSLGqQc/rbNpxSm7JjprrYp/Y9Ek5izE2OCSO5Vx8KLKNIRU+r6IUk+PF1N+BJ4feINDvj3nUQDB+5zoTNLfnkFD6B00IFjAHfJo9S1k1YrTFuL6yEHtdw8PkZOTrbW35QtAA/0udoVUDfRdVQNzt8g0gMB25nXssMk9/wEGsnPuv/h7riT/6BSimRYwpbNpEw+t2sScjRD/sGrj83YiEV8CKhu59zDoES73trKh72aR6UMEarhjvejKXpjmkSvJ3nGnud9cSW6+C7F8S87P1RkgEf8Vw+aw/mknxsQ2cqIgAywwMCcwLInLncE6XdhIPwA17Cte1L/TmWRTMM3NuNLsLExxydOIvWACU2U2XeJ3DJqxEokZCWojxr6iwOKvU0oJ+YpG+lnFLl4nRbGR1wH1xVyIoMzxgHumAwmkAwkaiBjyGOpRSmsObLteYa+KVR8iJ0h71Xi1tUIsWfrK/37rQNmNj0af+tEXXTt95VqJSAdKwuFw/ko6LSvTm8tDaj98/w2PDCtp3AmS+iN+ld/vz81sBpBseUg5tVqdW15en/cPBrOsGJ24tfcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cache Partitioning diagram&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/5ba8ec33eb29270e9ef16f25a189ca75/3c492/cache-partitioning-dark.png&quot;
        srcset=&quot;/static/5ba8ec33eb29270e9ef16f25a189ca75/ff46a/cache-partitioning-dark.png 325w,
/static/5ba8ec33eb29270e9ef16f25a189ca75/a6d36/cache-partitioning-dark.png 650w,
/static/5ba8ec33eb29270e9ef16f25a189ca75/3c492/cache-partitioning-dark.png 1300w,
/static/5ba8ec33eb29270e9ef16f25a189ca75/d544a/cache-partitioning-dark.png 1950w,
/static/5ba8ec33eb29270e9ef16f25a189ca75/e4d4a/cache-partitioning-dark.png 2198w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This approach is often used in distributed systems, where multiple application instances share the cache while remaining stateless and decoupled. However, it can also be valuable in monolithic systems that require high-performance or large-capacity caching within a single application instance.&lt;/p&gt;
&lt;h2 id=&quot;how-to-distribute-cache-keys-across-multiple-instances&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-distribute-cache-keys-across-multiple-instances&quot; aria-label=&quot;how to distribute cache keys across multiple instances permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to distribute cache keys across multiple instances&lt;/h2&gt;
&lt;p&gt;In an ideal scenario, each cache instance would manage an equal portion of the total cache data, ensuring efficient use of resources and consistent performance. Theoretically, as you add more cache instances, overall performance should scale linearly-doubling the instances should double the throughput. However, in real-world systems, achieving this perfect balance is difficult. Disparities in data distribution often lead to uneven load across cache nodes, a problem known as cache hotspots.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/d302b8432c10e5bcde34ff50de76a516/e4d4a/cache-hotspots.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 82.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAIAAABSJhvpAAAACXBIWXMAAAsTAAALEwEAmpwYAAACZklEQVR42o2T30sUURTH9/+I/oHeQgh6jx6S3kpMpaRAI32wh/CleggsQswH2SiCck1SSR8qjCSDJIJccttEaDF/NLvuruOMu3Nn78yde++5dzqzu2mhwR7uXM7MnM89h3O+NxYeZlrvO3v+QYv9/aJUFJjKWG0359tvzXfc/nCuf+7p6x/Rr8PO+AcGiCIm534ea5lqvv72TN/s8Y7prrsL1RIag6ferx1pfn6ic6bp4szRs+NdAwsNZa4dv2XS+ER6+MXyw8nvDxKphVSxocz1FkkRglQAkotQSxBSqlD/r2FSYpgIheCOwxzXr3Dq88BxCBUBl+Az5vqEBq7HQWnGmFJqH3ZdUsjnMJtZzO9YFn7Zte1ivlDaLe1sm1iwUpDbylq2FfAgl9tkzK/DWA8pVWybVHhYLAcgBT5GbsMoGpmtXyvGZpk6CrRZoNaGbRWosenumKwOf8yIvjHSP0lvTFR6xtzlbPg1lz451HYqfvn0o+6modbhTwnPDu93Ze9d2hjszt5pN0YHtuvw+GfWGifXRt3eROX8iDOd5GHIPS4o90seEQDo8EBJoTnTlEDVUbWhxqa+sCtPyPA7f3DW63xMXi0hLLExK8XVRSPNQQgQCGOolRerSx6jgP1SNfhlMrgQJz0J9+ozt2XEefMNYcEBbK9s05LAef2BPReIjYPRIKMVwVZFLa6LxTWRxH1dlKkGJTgWh03WChQwGUiMFtVLgjpQIfK1WxA7OHpUIkokUomKcoCOJIIeHlhbSur9OdfuXX1VvxLHsSzLNE3c9yTRgDyrxjkPgsD3fc/zDlW1rtpvXB6nq2Nh658AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cache Hotspots diagram&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/d302b8432c10e5bcde34ff50de76a516/3c492/cache-hotspots.png&quot;
        srcset=&quot;/static/d302b8432c10e5bcde34ff50de76a516/ff46a/cache-hotspots.png 325w,
/static/d302b8432c10e5bcde34ff50de76a516/a6d36/cache-hotspots.png 650w,
/static/d302b8432c10e5bcde34ff50de76a516/3c492/cache-hotspots.png 1300w,
/static/d302b8432c10e5bcde34ff50de76a516/d544a/cache-hotspots.png 1950w,
/static/d302b8432c10e5bcde34ff50de76a516/e4d4a/cache-hotspots.png 2198w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/e5171e1dd1805a8ea02626197fcdddfe/e4d4a/cache-hotspots-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 82.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAPoAAAD6AG1e1JrAAADRUlEQVR42qVUbUhTURg+bnPOj92ZUpZlK3ejqQjZIOyHjILqulzTraOmUkFhUURJ5NKy04dU64PyR8Yoss1N42Clq2xrwSW3xGiTZSv7wkhECOpHVvQnOJ27ZWXRh3Tguc/Luec85+HlfV8A/rhIDP1EQSLx5BdCRCSwqeFBgXFvMGjcEwxQvmdsCIaNewN10TNI9M+CWsRLBC5rfFoNDzwixob+ISr4yrQv9BkeGrzx9dXJC5Y2Pt9YeXyEwP0D7037Bz5VWIZJeePTa5N2CCAUC1S1JzS31Ow/btjddxrW+o6Zan1n9OY+ww8O/z2nhB7mAZBVAZBYNEOTYFBqk+meJJ/JTwEQS/8qZrVaY1t4XoYASGgtKJiBFnGzClRlGQbEJ7fp9Wl5qyzpNbg3/ohi9pRKlpul29IyfemGc2maamusx+NJxBiLJwj6fL4pvBuzggtvhzPTcd6hFPZvX8Qzu+xdLD6LZ95svzpHyB3BUOxyO9Xt7vYMu8eeyN/tUHsoTxBcu6RatWm9ZR6A7pTcytb5AeoYkrD0SLM57+itU1nIjxdsvWJbfPixLRUBXrLD6Musz61fuN3Azyk3efOKuW7VV6kYkFTo2KxYYXsnX2EfYXSOUYoPYGFryQni1JhH7IP1bxy95rfOHvPrtid1Y/bapWAgrTTnfm+Fuq+/XB3sKcsKPlydE7j4La+MHjcpil2E0Tk/ynWOsWTjDSJfeflQpIQwTkJhnLSbOqsOWGN3+S3ymvzheI7tjoOUi9Xe1PFYq42WG2AKHacUK9uInLvAM5zNr9BjGjsORn7SpJtfNuftGjqj3fasKY7r3kYv98ZHqkvFs5C9vQxmehUIENF3wSKnRVHcJTh8zxRd+pBccp0kFTobokXeIqsbbZq680XzNBRG0nU8ktWMC2a7U8rn30rXKltkSEsk6JvgEquK4eyVTKGtgtG1rhFi2fILGYRAseCK0KEgABMsrhk+GQ+zsRRmh6XjgyPiTsnLIPipdH4ZEgSJKCQCIBUbhyAkCArg2GdxAms0gdgf78bQ6hdPQPR14PV6U/1+f/od950MgalTEfifxdPucblcCaFQKLGzs1NOe/K3LceyXJxSqZV9ASZ+Z9h59HmEAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cache Hotspots diagram&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/e5171e1dd1805a8ea02626197fcdddfe/3c492/cache-hotspots-dark.png&quot;
        srcset=&quot;/static/e5171e1dd1805a8ea02626197fcdddfe/ff46a/cache-hotspots-dark.png 325w,
/static/e5171e1dd1805a8ea02626197fcdddfe/a6d36/cache-hotspots-dark.png 650w,
/static/e5171e1dd1805a8ea02626197fcdddfe/3c492/cache-hotspots-dark.png 1300w,
/static/e5171e1dd1805a8ea02626197fcdddfe/d544a/cache-hotspots-dark.png 1950w,
/static/e5171e1dd1805a8ea02626197fcdddfe/e4d4a/cache-hotspots-dark.png 2198w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;When cache keys aren&apos;t evenly distributed, some instances can become overloaded while others remain underutilized. This imbalance causes performance issues such as higher latency, lower throughput, and reduced system efficiency. A common partitioning method, mod-N hashing, which uses the modulo operation to assign keys - can create skewed distributions when the key space isn&apos;t uniform, concentrating traffic on certain instances.&lt;/p&gt;
&lt;p&gt;To address this, consistent hashing offers a more effective solution. It arranges cache instances on a virtual hash ring and assigns keys in a way that balances the load more evenly, regardless of the key space. This method significantly reduces the disruption caused by adding or removing cache instances, since only a small number of keys need to be reassigned. You can read more about consistent hashing in the &lt;a href=&quot;https://highscalability.com/consistent-hashing-algorithm/&quot;&gt;Consistent hashing algorithm&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/a54f8290652b8b2c657db53971234112/3da62/consistent-hashing.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.3846153846154%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsTAAALEwEAmpwYAAACIElEQVR42oVTW2sTQRTOvxIEBV98Lfiq/0GEPPgXWkGKFqtWEQqpVaG2QVub2DZro9EkDZs2aTCNMTX37O5sdmd3speZWWd2aqy44mHgnOHMd853LhMLfgml/AjLw4Hj0/OuSIkJRQj3d8aDtzUp9RhsSShZNuYy949kVeti5sKERINDIP3SCNbl+rPCqnJKLEjgxC8MS9JrJbMF820cRCWPCUqllJkrOwDR8ySFNlDwvmotF9cnvhNSoF2d9MecK8/cBJ1Xy02osMeU0ZuCmUVDVg52FvZWjyuA2bmGG19BtxLoqO1z8NLnxHY986/CRCxDxRv3FGa0FDK3ObqzqXYB5eCdqqdBQZgihCCEdiimabKriMDYpqsuC7Rbz16av3F5/nq+JXPwQcNTDDfsOVFVtd/va5o2Go16vR4AQOSHCKWqQ2a8q+9dfHDtwsLMfivPwfsv9MoHm9PGET31w1o+toqzO4vMOK1NZm9W7sZryo+wZmtMl6SNgaWI5D7GrHjGk6uwYRDgtUcD0+bd/pQc377ajF/5Jmfg2ZKslNaScntoiML/aJUCiXToHOcscXUn5GvROpFtz6XTJQnKPSxl7e2nIN2QTkZqXyPNHFmUnj/Mpqvts8FFr6cYkm3iTt17Uki8qbR3D4PcSyh/b/bMgdiNKR32Vsw09vfHID6ZIDYpy/VssWf/+Ri/Q5DAhKau64YxBkDnHYuC8t0j9Cdif0MhZm9TSwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Consistent Hashing diagram&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/a54f8290652b8b2c657db53971234112/3c492/consistent-hashing.png&quot;
        srcset=&quot;/static/a54f8290652b8b2c657db53971234112/ff46a/consistent-hashing.png 325w,
/static/a54f8290652b8b2c657db53971234112/a6d36/consistent-hashing.png 650w,
/static/a54f8290652b8b2c657db53971234112/3c492/consistent-hashing.png 1300w,
/static/a54f8290652b8b2c657db53971234112/d544a/consistent-hashing.png 1950w,
/static/a54f8290652b8b2c657db53971234112/3da62/consistent-hashing.png 2301w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/615c53c93546c324d0f63ab300ff1e19/3da62/consistent-hashing-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.3846153846154%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAC6klEQVR42mNgQAb19UwM//8zwvn28zkYXBdxI6lgBKshBoSGrmIGm/lmsUz5u4X+SXJXypVt1lWLRC+s7vq/qCQ6fJ9zuPh1BZCaVasganEDqK1M1ovjLbsWZXf9X5IQyn5FxcJih5BW4Wyh9v9zDOONzqQ66R/MZvKdFQl3LQ4AlkhSuRys5LA6nsF/nixEGMnrMLbTMnHR0EXZ1R8XJU78v40dLJY2k5XbdZEOj9t8DQaoLxlKXi5QjvU92hDBcFcc7H2wlxAG1v+vZ4IFyar/q9hK7y0vDQs6aAfi83otjuF2W/SKx33xe16fJZ5gDZ3/l6a0/F/ojjAMZ9iAgyZC8ZJ4vMqFVhCb02mBmXjYgj2iIQt2c9vN0wUr43ZfkM/hOEceEpz1TPv37+fZvXs3/5EjR3hB+PDhw4IgMXjk1+9n4fVcWMTA0MvZ+3+FY+2bpWfr3i093fFrqRlYgajr0lCt2FVy4ND6/x9koMTevXvl9+zZIw40UOro0aPyQCwGMzBt1W5+1ZzF6SA2yGcNfxacbvi78Gz9/4U2YAXJqpfSo5XOe4PYxsYzWXF52H5/PQuIrv2/xLrl15JKEDta44xRov3JXQnWJ7fEyJxVASv0kDgkWnhxRUH3/0VikEgAuvL/fhb7+noWUGTU2+9ngUVKosQ50UjDk22h9auEQPw4jctxscoXnsWonH8br3PNG2575/9lkVpFwHDRAUY/jmTD7rtIUcJlaWaCyjlXmFia8RmuaO3TDrFaZ2zi5fdzwDSAY4/Zb6afodX2jHilCxXN/xe6OE5fpC9hs0orjOG6Y8ObFbkh2xfVMGgu9EfODLizHjS5ROleEvSXP2kA9H6iRuWibG6rJYWJ4jeSMzautWj5v1gSHJb2kLCE62X4zwzCmLkHZCvU5v+r/jM/PXOG68qV/Tz/gYkZ7nViCwdUg/8zbTm8RfDQoUOiu9ftFt64bI946KpQZob/uPOvvb09i7GxMRcAPs0pXOwxp7sAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Consistent Hashing diagram&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/615c53c93546c324d0f63ab300ff1e19/3c492/consistent-hashing-dark.png&quot;
        srcset=&quot;/static/615c53c93546c324d0f63ab300ff1e19/ff46a/consistent-hashing-dark.png 325w,
/static/615c53c93546c324d0f63ab300ff1e19/a6d36/consistent-hashing-dark.png 650w,
/static/615c53c93546c324d0f63ab300ff1e19/3c492/consistent-hashing-dark.png 1300w,
/static/615c53c93546c324d0f63ab300ff1e19/d544a/consistent-hashing-dark.png 1950w,
/static/615c53c93546c324d0f63ab300ff1e19/3da62/consistent-hashing-dark.png 2301w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Consistent hashing is a key technique used in distributed caching systems like Memcached and Redis, often implemented via tools like Ketama, to support efficient and scalable cache partitioning.&lt;/p&gt;
&lt;h2 id=&quot;how-to-decouple-cache-partitioning-from-application-logic&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-decouple-cache-partitioning-from-application-logic&quot; aria-label=&quot;how to decouple cache partitioning from application logic permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to decouple cache partitioning from application logic&lt;/h2&gt;
&lt;p&gt;The application’s logic should remain decoupled from the cache partitioning mechanism. In other words, the application shouldn’t need to know how the cache is segmented or how keys are distributed among instances. Instead, the caching proxy layer should abstract away these details, presenting a unified interface that makes the cache appear as a single, cohesive system.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/c2838e2db52700f58d583eee0ea6f356/95fa1/cache-partitioning-abstraction.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 121.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAYCAIAAAB1KUohAAAACXBIWXMAAAsTAAALEwEAmpwYAAADQklEQVR42o2UaU8TQRjH+Qq+8AuYGJEQNdEQExPkhRj1hReFaEKEFwoENZJgFAJBAZEjBuMLMRAx3FAgYuVGLXJoEIWCSDna0pYrpXv12O45s+u0S1sgG2Uy2cxM9j//Z575PRMh769JkspiRGgEgKR0CHf9qEzsOBhfFmbsogDUxOqGsgwhoNz0oxYit919u5aasgpoXdk/LG7/bClvMFQ0GPomHDSPdBAtMizr9XoZlmv6JlZ9kiv7oRWDYbFyGJYDsWkfIhNbD19vflJvthN+V5blfD6fsrWNcPTNT/20L0EpHHdQzIMLD3rPputSC/XFTab5VYbAnRTlEgRBFEU37TldmRBVGn8w79TAwqg/QRDsFZ+7232nZPhZs/mPzYdjWwRBsCzL8zzppq7Wpp+p0kS/uDBs+qEijsvQHU1qi9S0PG2wrJH+sDmOR2FLgT9sm9jQ95mf85addxFOWP/31er3xpou47ABY4TthCFnmqZ9NPM6a7UieaNIY1+e9mdB2SLif3AgPKCLpEuSzQUJ5qy4xd9jXr8YSPuDJDDbtPKzo96lKZ8gSPuFZBdl/8ATGYZawNAfAgwyPWIU6keZjgmWoqVQOCrOAMCgXrGUnIQ7uwnP0XpSa8hJSwBPqECiCKCU/2YyKfdTUu5gdbedk+SvU+uax4OzJkICPMfzEya5a1LW/ZIpOpyI7XtmODEu4+PJW50xKZ1FjRavKL/TGaNvaHvGbei+UQD9i/rioVdV43Uu1qUcahckl7J649J1mWUj5VrrOgnqP85ezBpo6jaIjGcT2zpWGH+i/MqBh8e7Zgb2Eoacz9/rQXgm5gyWtq24eLmxdzE2TacbsciywAug8MvLmx2ZyR33F7bMgaqCYbEgwsvZ/VFJ2iOaloI6EwPl1oGlQ1eb9b82ZMghn41lfqwHmxvzQEHtqjactNFKLVipNSeDMPGx4oKNQseRJIA5XfnXlvMvr2TGLE7r3SqE7YEidFv+jDD827zVshTr82TryhyjwnYAElnpITBDaAms5MKAlwTqhCEpenE8Hg8qI2WAvqgk0cw/pr2oQPc8ozudIY7jDoeDoig82DAMQ08CSZJogDlxKVgzCrx/AaukBUWpPTiTAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cache Partitioning Abstraction&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/c2838e2db52700f58d583eee0ea6f356/3c492/cache-partitioning-abstraction.png&quot;
        srcset=&quot;/static/c2838e2db52700f58d583eee0ea6f356/ff46a/cache-partitioning-abstraction.png 325w,
/static/c2838e2db52700f58d583eee0ea6f356/a6d36/cache-partitioning-abstraction.png 650w,
/static/c2838e2db52700f58d583eee0ea6f356/3c492/cache-partitioning-abstraction.png 1300w,
/static/c2838e2db52700f58d583eee0ea6f356/95fa1/cache-partitioning-abstraction.png 1816w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/9e2acd3e110f2f938834d5b85e8cbf9c/95fa1/cache-partitioning-abstraction-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 121.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAYCAYAAAD6S912AAAACXBIWXMAAAPoAAAD6AG1e1JrAAAEWklEQVR42mNgIA0wQjFxIHTVKmb7+v0sIMxgX88CFGJCNew/2DB+nxVGvF7LE3i9F/sx2M/nYCADgA37/7+eKatrlYRo8JprfD4LHwj4Lf8p4LvOH+oaZgxdQdVnMgNrzkyOaL3Wpe7VNZlByt0SZuCqVat4tixdKpg7cTEft+PCOaxW83ez2y04yum21Aysov4/zDcQb/jUn+GKaL39IKz5+r+4nicflbwn/GeQ828HyXV2zuFdvHgxH8zizEtLlWIPLA1NPL7U5v///+hhijAwuO7CA6ALP4TWnTus7Nn5X0A3rgeoQWDmzMWS8+fP51hVv4pt9rFVQs3fV5xp+LL4XuO3pZ/qvyz1Aulf9R/uZTQDq8+8Dq0/v0fZo+O/oEFCP8jAWbMWy4C8fGzVKs5FR9eKNXxdsr3ux4Iz9d8X3ar/vMwJHITYDIxouXU/tOna/9juxx+UvPr+M8j4tYHksurrebZt28bHAPVe0cKtavmzt4bnztniWF9fz4QzKoNqzkQFVp9uCG+6Vq7h013HIOVhCIuURYt2cq9fP1+gv369QJzmuc1RCufORiufuxaledYO7EIGLLFMKEGDks2Eyj3iiboXT0Zrnr4dq3XuY7zGeW+wgaH/MQ0MDSUuYUdrndOK0DrrF61+2sFD5RY7MVkLPYvB2f9JyXqgAIZhIA/kOiZQOqsHJVpI4IMNE/RckiHgvniOgMeSXiHvBdLIkUscAMcuREP1lC3yYqHrbvB5L3gs4L/qD3/QpgD0rMcIKxiC684vDKo9dzGi5cYxZY/WGwycBv5JE144A9Pmdf+yU1b//4cyr1p1jJPJdEEKs/m8ScwWC3oYbOfJorkQwkgDpsOwluuPghsufY9qv/1CybP3PwOXWWnqlNfpoY1X/oc0XIr+/x8Y00D1ff+Xu7X/X1HT/X9lxsz/q/ihgYtqYGjvI06gC+8FVJ/6AMwp2xRcW/6zKno25E6+m+1bdvR1esfRkv//J7JPPbVDtuPbxuvVrxffbfq0+kf7782BuLNe/YVHoKwX3nT5jCLQDQz8VmXJfU9T/CuO/QxvOpMAUpc7cRt74//5DU3/5y9r+r9gSdO/hZqQwqYetbTxyN3GHtJw8Xpo09X/0e13v6n49P9n4DTMTZn0Mi6y/d5/v6ozgUAvM4FcEqp0UTfe/HxCrM45rzSG/6y4S+zqE4qB9ReNIlpv6OkFzzFkEPLgS5v5lCu45oKhcdoZLlBO6WneIZuse/VytNqZB3Gal74n6F71x5lTsKQXpAQO8cmi7gvcCbpnlkdrnDkdrXH2XIzmBXOwlxn+M2HNemCNoHRXz8CEMAy1RAmVecTpJ3tWKlHlnCiB9PufccuWLYL79+8XABVXu3fv5j98+LDg+vXr4fwdO3YI7T6zm//R/1Wc+LIoPOvt2rVLCmiI3Nq1a8WABkts375dEoRB4kDLJPbs2SO+efMe6c2bj0kje9Pe3p7F2NiYCwCm0RBfO4c5UAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Cache Partitioning Abstraction&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/9e2acd3e110f2f938834d5b85e8cbf9c/3c492/cache-partitioning-abstraction-dark.png&quot;
        srcset=&quot;/static/9e2acd3e110f2f938834d5b85e8cbf9c/ff46a/cache-partitioning-abstraction-dark.png 325w,
/static/9e2acd3e110f2f938834d5b85e8cbf9c/a6d36/cache-partitioning-abstraction-dark.png 650w,
/static/9e2acd3e110f2f938834d5b85e8cbf9c/3c492/cache-partitioning-abstraction-dark.png 1300w,
/static/9e2acd3e110f2f938834d5b85e8cbf9c/95fa1/cache-partitioning-abstraction-dark.png 1816w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This abstraction can be achieved using a client library that supports consistent hashing - such as Ketama for Memcached - or by introducing a proxy layer between the application and the cache. The proxy-based approach provides greater flexibility and scalability, as it can dynamically adapt to changes in the cache topology (e.g., scaling nodes up or down) without requiring any modifications to the application code. It also enables switching partitioning strategies seamlessly, making it easier to evolve the system in response to shifting performance demands. We&apos;ll explore this in more detail in the next article.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In the next two articles of this series, we’ll dive deeper into designing a scalable caching layer in distributed systems.&lt;/p&gt;
&lt;p&gt;The second article (&lt;a href=&quot;https://handsonarchitects.com/blog/2025/partitioning-of-key-value-data-in-cache-systems-part-2/&quot;&gt;Part 2&lt;/a&gt;) will trace the evolution of caching implementations-from a basic in-app caching library, to an externalized sidecar (ambassador) container, and finally to a dedicated proxy layer. This progression highlights how decoupling caching concerns improves scalability, flexibility, and maintainability.&lt;/p&gt;
&lt;p&gt;The final article (&lt;a href=&quot;https://handsonarchitects.com/blog/2025/partitioning-of-key-value-data-in-cache-systems-part-3/&quot;&gt;Part 3&lt;/a&gt;) will present a hands-on example using Twemproxy (Nutcracker) as a sidecar caching proxy. We’ll show how to deploy it within a Kubernetes environment alongside your application and configure it with Memcached and Ketama hashing to enable consistent, efficient cache partitioning.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Making better architectural decisions thanks to slow thinking]]></title><description><![CDATA[Have you ever made a technical decision and later questioned if it was truly the best choice? As software architects, we've all experienced…]]></description><link>https://handsonarchitects.com/blog/2025/making-better-architectural-decisions-thanks-to-slow-thinking/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2025/making-better-architectural-decisions-thanks-to-slow-thinking/</guid><pubDate>Mon, 19 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/b5bb25760683baf441fcb61a4be63544/2bef9/software-architecture-slow-thinking.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEXklEQVR42mP40mv2pdf8S5/5VyCjzxyKQGyzr0BBENcMjJDZUBEGsGYQ+t5v/meSxe+J5j8nmAPZQBGoarDsV7i5EBEwG6QZaOefieZvu00vVhlerzF80GT8usMUaMRnhFsgGsy+IBkHRAxf+8zedZqcLNGvc1ZON1bIMVMEkl2+Km86Tb/1mX+Ga+4zA5r1uRduCljz38kWu3N0IvRli2zVCu3U8q1Uqh3U4w0U1iRp/ZtiAbYcpAdo0K+JFr8mWXwDhQjUOIbv/WYtniopBgpXukJez4y53h26o8w93VbtRJHev8kWYNtAofC+2/Rek9HDFuNPvaa/JoL1A23+Pcl8TphamqHCsXrfKRmuU9KcZ5WFNEZbP2o0/DHB4lOP2Y8J5g8bjfZmWp3Od9+Z7rgpzfp8hfGPiSDnMPyeaLa7wLjUTe98a8ClTa3H5xXeXlm2pinybr0h0MJv/WbPWoxWpzucaow7Vxtxoz7iWmnQzkyXe43GQEMZvvaaHK20W5DhvKfC8+ea3AtdEbtKXKcn2O4rMPo1ARi3pmeKTLY1xG+fW7pqVumthSUvqwNuV4ceKTT70W/KAIykkxXGIS4G5S5aG3Kd8q1Vit204wwV272Uf0+yAIbIqRKjq9Whp8qCLjTHPm+LuVbmf7YocGe2+bc+U4YfXYalGc6Krg5hmhLpBrJVDho9YaYr8t225tu86zIBppZ3XaYnCk0O59ruSLdZn+S0Idl5T5b18xYjYHJi+NhmEBFqox8b4ujnUO6mk2ksPy3B5u/ytMP1ns9bDL+DXA6MJ2Ccmzxs1FmbpDszSv9IhenuMus7jSYM6/JN9FNirLJijdNj/DPCPCI8QsOcf86NjQpzXJxp9H+y2fsu098TzI5WmFXkxDTX5FTXFBZUlSfn5Z0oN2bISnTUK82xSAq1zEs0jQ40LU63SI/2DnI0jPD38TZ/1aT3qc/iZ5/x9EyHgqbmxq6W6qbqlv7OuqKEF80GDO6x3qZZ8Wb+rkaZ8XpOViY+ToY5iYbRgTblWbqpsZsLTf7PsPjRa1iTHlDf3ZpXmBmXGFnV0TY53+drlwGDc5yvSVGGSXywW4Dj/vbw/RWesdEuJi7mlklh2vmZNdGmJ0qNpsfpFeWntE/obGivr22tL2ps6I403J+hxlAUbGCRGKYa5FOb6fOoO2hntW9TrP3kNLuuQJVIF93wIC8HdzcTa9vk3Kzs0sKkjOTk2ODcUIcN8SpnslUZ9iQqrYpSmhumuLjC7+ia5qNrmrYurr+we9KFTa2b6/yXxmrMjdWrCTGtDDLqDdaYG6K4NEJxTaT82hillZGKDGdz1Y9lqR1OV9kZr7Cl2vP0htYNk/NmVoYvqgmbne/R5as0P0B6XoD0HD+pad7SXW5SHa5SQLLTVbLNSZJhQbD8rAC56X6yM/xk+52F6x0kq+ylKs0F6yz4m60FWh3EWxwlOp1BqNtZYrK7xFQPianuElPcJaZ7SAAAyn8r6kvX11gAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Software architecture&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/b5bb25760683baf441fcb61a4be63544/2bef9/software-architecture-slow-thinking.png&quot;
        srcset=&quot;/static/b5bb25760683baf441fcb61a4be63544/ff46a/software-architecture-slow-thinking.png 325w,
/static/b5bb25760683baf441fcb61a4be63544/a6d36/software-architecture-slow-thinking.png 650w,
/static/b5bb25760683baf441fcb61a4be63544/2bef9/software-architecture-slow-thinking.png 1024w&quot;
        sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Have you ever made a technical decision and later questioned if it was truly the best choice? As software architects, we&apos;ve all experienced situations where quick, intuitive decisions seemed correct initially but later created unexpected problems. This is exactly why Architecture Decision Records (ADRs) are invaluable. ADRs encourage a deliberate and analytical approach to decision-making, known as System 2 thinking, a concept described by Daniel Kahneman in his influential book &lt;a href=&quot;https://www.amazon.com/Thinking-Fast-Slow-Daniel-Kahneman/dp/0374533555&quot;&gt;&quot;Thinking, Fast and Slow&quot;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;adrs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#adrs&quot; aria-label=&quot;adrs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;ADRs&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Architecture Decision Records are short, structured documents that clearly record key architectural decisions and the reasons behind them. Typically, ADRs include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Context: Information about why a decision is necessary, including background and relevant factors.&lt;/li&gt;
&lt;li&gt;Considered Options: Alternatives evaluated while making the decision.&lt;/li&gt;
&lt;li&gt;Decision: The chosen solution, along with the reasons behind it and expected outcomes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Writing decisions this way forces you to explicitly compare your options, clearly showing benefits and trade-offs.&lt;/p&gt;
&lt;h2 id=&quot;why-context-matters&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-context-matters&quot; aria-label=&quot;why context matters permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Why context matters&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Context is essential because architects often need to compare technologies that initially seem very different, like comparing apples to oranges. While these may seem impossible to compare directly, establishing clear criteria (like comparing fruits based on &quot;skin hardness&quot;) allows for meaningful evaluation. Similarly, when choosing technologies, clearly defined context reveals exactly what attributes matter most.&lt;/p&gt;
&lt;p&gt;For example, comparing a serverless solution to Kubernetes might initially seem impossible. However, once you define the context clearly, such as &quot;ease of scaling under variable load,&quot; you have a specific criterion to help compare the solutions effectively.&lt;/p&gt;
&lt;h2 id=&quot;activating-slow-system-2-thinking&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#activating-slow-system-2-thinking&quot; aria-label=&quot;activating slow system 2 thinking permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Activating slow (System 2) thinking&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;According to Kahneman, our minds operate in two modes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;System 1&lt;/em&gt;: Intuitive, fast, and automatic. It relies on immediate impressions and quick judgment.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;System 2&lt;/em&gt;: Analytical, slower, and deliberate. It requires careful evaluation and logical reasoning.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Architectural decisions often have long-lasting consequences, making them expensive or difficult to reverse. Relying exclusively on intuition (System 1) can be risky. Taking time to create ADRs explicitly activates System 2, encouraging architects to carefully articulate context, consider multiple alternatives, and thoughtfully evaluate their implications.&lt;/p&gt;
&lt;h2 id=&quot;improving-context-and-decisions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#improving-context-and-decisions&quot; aria-label=&quot;improving context and decisions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Improving context and decisions&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;In practice, your first attempt at defining the context and options is rarely perfect. As you explore alternatives, you’ll often discover additional factors you hadn’t initially considered. Regularly updating and refining the ADR context ensures your decisions become stronger and more complete.&lt;/p&gt;
&lt;p&gt;Imagine deciding on a database solution. Initially, your primary context might be focused on read performance. But as you dig deeper, you realize ease of operational maintenance is equally critical. Updating the ADR context to include this criterion encourages a more balanced, thoughtful decision.&lt;/p&gt;
&lt;p&gt;Another useful practice is clearly documenting Architecture Drivers (key architectural requirements or principles important to your project) and reviewing them with every major decision.&lt;/p&gt;
&lt;h2 id=&quot;joint-evaluations&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#joint-evaluations&quot; aria-label=&quot;joint evaluations permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Joint evaluations&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Kahneman highlights that joint evaluations (assessing multiple alternatives simultaneously) lead to better, more consistent decisions than evaluating options individually. Single evaluations, based mostly on intuition, tend to miss key differences because they lack a direct comparison.&lt;/p&gt;
&lt;p&gt;To illustrate, Kahneman describes an experiment comparing two used dictionaries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Dictionary A&lt;/code&gt;: Published in 1993, 10,000 entries, condition &quot;like new&quot;.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Dictionary B&lt;/code&gt;: Published in 1993, 20,000 entries, slightly damaged cover but otherwise &quot;like new&quot;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When evaluated separately, people preferred Dictionary A due to its better condition. But when evaluated together, Dictionary B became clearly preferable, as it offered twice the number of entries - an advantage that was more obvious when compared side-by-side.&lt;/p&gt;
&lt;p&gt;This insight directly applies to software architecture. Instead of evaluating technologies individually, use ADRs to evaluate them side-by-side. Joint evaluations help reveal key differentiators, leading to more informed and consistent decisions.&lt;/p&gt;
&lt;h2 id=&quot;lessons-from-other-domains&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lessons-from-other-domains&quot; aria-label=&quot;lessons from other domains permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Lessons from other domains&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Kahneman points out that fields like justice or public policy sometimes make mistakes by relying heavily on single, isolated evaluations, leading to inconsistent or unfair outcomes. Similarly, in software architecture, narrow or intuitive decision making can lead to costly mistakes or regrets.&lt;/p&gt;
&lt;p&gt;Incorporating ADRs into your decision making process helps avoid these pitfalls by encouraging joint evaluations, careful comparisons, and deeper analytical thinking.&lt;/p&gt;
&lt;h2 id=&quot;final-thoughts&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#final-thoughts&quot; aria-label=&quot;final thoughts permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Final thoughts&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;As software architects and technology leaders, the decisions we make significantly impact the projects we work on. Using ADRs intentionally engages our analytical System 2 thinking, ensures clearer contexts, facilitates iterative improvements, and encourages consistent, joint evaluations of technology choices.&lt;/p&gt;
&lt;p&gt;By adopting ADRs, you become a more thoughtful decision maker and clearer communicator, ultimately improving the quality and durability of the architectures you create.&lt;/p&gt;
&lt;p&gt;If you frequently make important technical decisions at work, we highly recommend reading Daniel Kahneman&apos;s &quot;Thinking, Fast and Slow&quot;. It provides invaluable insights to enhance decision-making in professional and personal contexts alike.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Who needs software surgery?]]></title><description><![CDATA[We recently watched an insightful panel discussion on YouTube featuring Daniel Terhorst-North, who compared software development to surgery…]]></description><link>https://handsonarchitects.com/blog/2025/software-surgery/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2025/software-surgery/</guid><pubDate>Tue, 29 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/38d1ed8a74780dd9654a0328722538a6/2bef9/software-surgery.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEsElEQVR42h2S609aBxjGz1+wJds+bEkvXupq0i5Nm9YNaRFQHKKi1iraqm11glzO4cABkftVriJH4SAoF+V+O4BcBCVrV5s0XdtlaZZsWZZ1yZZsyZbsw/6B7mzJ8/H9vU/yPA+AFI7mtkJUWHvlnpDGlzMEcpYAZisMwjgOZytwpiJMlISJIoIfSYvNIZn+gTcsTpdXoll+DAeGVNY+4Rp1CVxzaB/qdOie/dkj74REseCLQv+TvIMCP1a8u+Ef19pJixCZJyMty3rm+TMbO8CYwTUiNyyp105b6BQMbQVNSqdpUu+W5Y+QQh3J18BkGcnXKSuyD4cXu+ak7TPiNg50eWr5jm0beLgdGuQjedyTzLq4GkX0wEyXSka33By/TxjLExhhLslV6bDm7DR0YX61c07WOS+/Mrsy5fABS3upYblO6TZyLeYRhbETBukpFzXhIEVME5hvtdAQpUrSfJ0uXD0/8uAih9/N4V+a4V/lLE8TsDhT4cXw+6HsSrw0aN3skIrI6wjbLOboeOMmDZipCWIFIrCH3vCC2z+34V/wBO57AnObgeVwhoAPRcmSHK+JsrX7RqNWNi8XcL7ZVyfsUr3DxE1WhPGiKF2Rlo6RcgvKN5DisaLSkpdP4Gz1P1hANFGog+nqbZXxVRB6HFb5DrxOrx3CdpFSc9G/z14z3jE4pg32Ma2VqbGNWdDZzaAglgeg9CGYKiPZyqDds4ysoBpu7CTje/0y9PqZ6CApztXv6G1Pv8J/+eEUTUTNe8GTVv6wlpbolDSxChAmS1/spT5Vaa5iRiiojJi54H4Ue/HE0qgSafPipSWz9c9fX7z58WmsWfGVi3/99urt278j2QgF1gKyXJ1msd3Y0jJ2zQNp9xSmvqmEZr0BfjTDDaVGrT4tuoE/bqRrhZ18qtzIJxvln396rrCZx+1EVcF4txJh5Ty0gIkRXmfm0c+2VLOBwAwavcDVfDQljGSjh19Wv339GD8uv3zVCuKZ775/Oi1BFvdSwKjd0wbzR/IeZsJBCxjZOEp2r/XptDN2H+zfZUFybH9np5htntb3j2svnjcOGpXWk8MhSE6MF+i3Oc8hInrYStpQ9bqUwzn3WTGXBoOtk0y1HndhrsusiVsz87cFIshkiKbD8aPidhijC2VgogjctKyfAfndMtF1g5TkXKN7dZ7Y9j9/fP37m9McHiZP33uvh/7udeo71/re/7T/AxLjEnO8a2CUrrTqmo+AXoOxQy3rlEBkn4kec/SievWOL1ovsUQwm7dydWyqiznxyRinmzXZRhs+Q2G2j3CGdHYoV30QzgI9ak27Qtopk5yHwY8hyRVITdZ7Jv3xu1i0D9FdnFxoG2BfZI53DIx2jHBYWqukUBXjzQkscUuPAiSV9hws6kTgdhl0Ta6jG7Ypmo0h5+50MMPN1Pjpw7u+SJ9EzdLZ4UINxJvj3li/LUg1Y30GFGA5PWdhIQG3weANhZlq2KLoPYRuaTfJGvegK0RcL+wXeOkqG41SLX6GM8R0Ryg6D83kBe75wl3q1W6DqgMCe1UOhmOXZsEYzr1+2w7DHqSvY70qV6/G3SO3Ee8It89doQFbgKJHme7Iv3H/TDy4becKAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Software surgery&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/38d1ed8a74780dd9654a0328722538a6/2bef9/software-surgery.png&quot;
        srcset=&quot;/static/38d1ed8a74780dd9654a0328722538a6/ff46a/software-surgery.png 325w,
/static/38d1ed8a74780dd9654a0328722538a6/a6d36/software-surgery.png 650w,
/static/38d1ed8a74780dd9654a0328722538a6/2bef9/software-surgery.png 1024w&quot;
        sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We recently watched an insightful &lt;a href=&quot;https://youtu.be/86-Dy5U2p5Y?t=1405&quot;&gt;panel discussion on YouTube&lt;/a&gt; featuring Daniel Terhorst-North, who compared software development to surgery. He shared a powerful thought:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;No one wants surgery. What they want is what surgery gives them, which is to be well.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He points out clearly that people don&apos;t actually want surgery itself - they want the positive outcome surgery brings: better health or relief from illness. Similarly, our customers don&apos;t want software just for the sake of having software. Instead, they want the benefits that software provides, such as solving problems, automating tedious tasks, or improving their overall experiences.&lt;/p&gt;
&lt;p&gt;Daniel explained that, when surgery becomes necessary, people typically want:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Minimal intervention&lt;/strong&gt;: The smallest, simplest surgery that still achieves the desired outcome.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Experienced surgeon&lt;/strong&gt;: Someone who has performed this surgery successfully many times before - no one wants to be a learning case.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Proven procedures&lt;/strong&gt;: Techniques that are well-known, tested, repeatable, and backed by evidence - not guesswork or experimentation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clear documentation&lt;/strong&gt;: If another surgeon needs to step in later, they should easily understand exactly what was done, why it was needed, and how it was performed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These points translate naturally into software architecture and software development:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Keep solutions simple&lt;/strong&gt;: Deliver the smallest, simplest solution that meets your goals. Avoid unnecessary complexity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Value experience&lt;/strong&gt;: Teams and customers benefit from working with developers and architects who have solid, hands-on knowledge of their systems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use proven methods&lt;/strong&gt;: Choose architectures, design patterns, frameworks, and cloud solutions with a strong track record. This reduces risks and improves reliability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Document clearly&lt;/strong&gt;: Good documentation makes maintaining software easier, helps onboard new team members, and ensures knowledge is transferred smoothly.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;who-is-the-architect-in-this-analogy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#who-is-the-architect-in-this-analogy&quot; aria-label=&quot;who is the architect in this analogy permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Who is the architect in this analogy?&lt;/h2&gt;
&lt;p&gt;If software developers are surgeons, then who is the software architect?&lt;/p&gt;
&lt;p&gt;Following this analogy, &lt;strong&gt;software architects&lt;/strong&gt; naturally play the role of experienced medical specialists or consultants. These specialists carefully plan procedures, recommend proven methods, and make sure everyone involved clearly understands what&apos;s being done, why it&apos;s necessary, and how to do it effectively.&lt;/p&gt;
&lt;p&gt;Thinking about software architecture like surgery helps us prioritize simplicity, clarity, experience, and outcomes. It reminds us that our ultimate goal isn&apos;t building software itself - it&apos;s delivering value and benefits to users and customers.&lt;/p&gt;
&lt;p&gt;If you&apos;re interested, we highly recommend watching the &lt;a href=&quot;https://youtu.be/86-Dy5U2p5Y?t=1405&quot;&gt;full panel discussion on YouTube&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Effective planning for Site Reliability Engineering (SRE) projects]]></title><description><![CDATA[Effective planning for Site Reliability Engineering (SRE) Site Reliability Engineering (SRE) is a discipline that blends software…]]></description><link>https://handsonarchitects.com/blog/2025/effective-planning-for-sre-projects/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2025/effective-planning-for-sre-projects/</guid><pubDate>Mon, 14 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/c306f5a10c305bd4995b42cb8c6e652c/2bef9/sre-planning-hero.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAExklEQVR42hWQaUyaBxzG38/7uKbJtppmPWIbp1AvBK/iagFdRRkUEVHkPgovKmpFULkKIiCInIK+0MLLodV6VO1UGq32srNVrG111nZzS7Pv+z6a/PJ8+Of55Un+gCiYEI3H+f4oxxcWBOA0bG+Y6Qq1uENs7z0xFL8VnBCOxVneMMMZbHJCfD+cbqYRBycAnh8mm5wUo4PpgOosAZ43zHVB5AFnndZGt/oYgQlB8L4EipsT07OPn/SFEtVaG9k4UmdwcLxhID1C0tsJ/VaO3qC1akRmC80eYNhHKSa3eMTnGzV0+zzUYcg1OXv44WAgMknVDYltbpreTB8eA1jukAqC2/yxCa/yP5h7191Ds40JPFB3KJHWtjzCPquOMui7obZc6zHybN73Bwf/nhybIxGaAwL4Y7G17d1PJ1/uxsadBtEU7N/Z+zC7+VwIT7XP/caGonRXSBOeYA2Pikb8Rvj+/Nrm4rOt2/cmW3xhgOe7K3V4WweHGFZPrdU/Orf8/PWb8Obz9iGDopUpoOJ4oJDmgYYWV47e7X85OYkkN6g2HzgeTb8ZKMDVZqErvssq8MKJT3//83r/vXbuodznkOGvUDJPU7PPkC6dJtLJzpWV7dTeq9SeZ2G5bRwOTi30hKeAi7mlmfnlGT+hfPfg45O/9g8P3WurXXZdLxULtTUljeBSNxOPzjHNx4Zm4jNrG3OPN17u7c+sbQr8EeByUcWFK8UZlwuEeq0zOa+EoZGVB9HVqEMrX9LJw0oQ7uIqxA3Jjxvz28mjj8c7b3Y/Hx3vHPwhCESBy6iv8veZ+RK9Ora1HN1YXXi1srgGp6Y9O7rbf44Ovg3oDI21bTqFymr0BSMPV5Lvjo5mn77gjoaBS4XYC8jiHzLz2Lc7Z35f/vzp5MPhbvJZdLSTrq1BB0W1xptl5ZWE3ErieQTqm2/PVLHY5keLt8YjolACOJtVmJlXdjYbTROLnuwupfZfr6de3onHhIy61opLXMwFDCKrvKUddEAsjfUcAlPN5g4/3WyNPwCj08CpjIvnclA/5hQT6ulvDxbXt6ZV4Viz3kWSdiqJFXYKoYbX2dhvZ6gsfLMf2yDENzaPbb9qjSSk4UkgIxN5Liv/1HlEEaEm9X4ptBBv1o+IBr0V7PZeeUeoT35drJTYoQaFKQ2O00GRgPDb3WallGUxA1ebuaDH74nGYo8m13cfKUMxgrCnRTdyyxak91qJHJBj8AgsgfpuY52sl6X3yOwuYyKCo2EYnQIAx5c4X7xY30vNvlxVBQJNOndZPR/LBGk6D9PobdI4mJph3oCXKNPiua0s3XBzv0Xpd6FQ55s7RACqmiQdccuGLWKdRuIYpyn0Mr3G6TFFB9VWg6Gp18z5KgwRpSpal5qltVUJFRKjqRFsY/fpgCqeOLcCX4qrbgDbGpQmcvcdh1GddKkX1aDqV6JRJhyU8VlCKUmupsi1v3YYCXyFRKsR6U087QBwtZJQicMjC0uw+Gosvqrqxi8sOqnxJpFMqqGQa2+SibR6Kpcn4AhBsEsl6+6nNrEYHA6Vyyc2MABkUSkCVXKlqCS7AINEFeeiS5GoEmRR+lKahy5LZy7mamHZtcKyn9HY63mYckRhMaIAk52LyslH/w9OeU3Du4nieAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;SRE planning hero image&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/c306f5a10c305bd4995b42cb8c6e652c/2bef9/sre-planning-hero.png&quot;
        srcset=&quot;/static/c306f5a10c305bd4995b42cb8c6e652c/ff46a/sre-planning-hero.png 325w,
/static/c306f5a10c305bd4995b42cb8c6e652c/a6d36/sre-planning-hero.png 650w,
/static/c306f5a10c305bd4995b42cb8c6e652c/2bef9/sre-planning-hero.png 1024w&quot;
        sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;effective-planning-for-site-reliability-engineering-sre&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#effective-planning-for-site-reliability-engineering-sre&quot; aria-label=&quot;effective planning for site reliability engineering sre permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Effective planning for Site Reliability Engineering (SRE)&lt;/h2&gt;
&lt;p&gt;Site Reliability Engineering (SRE) is a discipline that blends software engineering with operations, aiming to build and maintain scalable and reliable systems. SRE teams often operate under constant pressure from production incidents, evolving technology, and organizational priorities. That’s why having a solid, flexible, and thoughtful planning approach is essential to ensure the success of both long-term projects and day-to-day operations.&lt;/p&gt;
&lt;p&gt;In this blog post, we share our approach to planning SRE projects, tasks, and initiatives. We focus on practical principles and proven techniques that help teams balance project execution with ongoing operational responsibilities, enabling them to deliver reliable, high-quality outcomes even in fast-paced and dynamic environments.&lt;/p&gt;
&lt;h2 id=&quot;context-the-nature-of-sre-work&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#context-the-nature-of-sre-work&quot; aria-label=&quot;context the nature of sre work permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Context: the nature of SRE work&lt;/h2&gt;
&lt;p&gt;The scope of SRE work spans several areas, which can be broadly categorized into the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project tasks&lt;/strong&gt;, such as migrating to a new technology or platform, implementing new features like rate-limiting, improving system performance, reducing infrastructure costs, or increasing service reliability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Operational tasks&lt;/strong&gt;, including handling incidents, writing postmortems, maintaining runbooks, participating in on-call rotations, and setting up and managing monitoring.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cyclical tasks&lt;/strong&gt;, such as reviewing Service Level Objectives (SLOs) and Service Level Indicators (SLIs), preparing availability and performance reports, and conducting regular audits or reviews.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Multiple SRE projects and tasks often run in parallel, with different priorities, varying levels of urgency, and shared resources. This complexity makes planning and prioritization particularly challenging. Additionally, SRE work is frequently interrupted by unexpected incidents or outages, which can derail planned work and disrupt timelines. To make matters more complex, many tasks and projects are interdependent and require cross-team coordination and collaboration, making communication and visibility critical components of success.&lt;/p&gt;
&lt;h2 id=&quot;why-effective-planning-is-crucial&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-effective-planning-is-crucial&quot; aria-label=&quot;why effective planning is crucial permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why effective planning is crucial&lt;/h2&gt;
&lt;p&gt;A thoughtful planning process is the foundation for effective SRE work. It allows teams to prioritize and manage their workload in a way that is both realistic and sustainable. Good planning ensures that technical goals are aligned with business priorities and stakeholder expectations, while also accounting for the team&apos;s operational responsibilities and potential interruptions.&lt;/p&gt;
&lt;p&gt;Without proper planning, SRE teams may fall into cycles of constant firefighting, burnout, or lack of progress on strategic initiatives. Planning helps bring structure and clarity to the chaos, enabling the team to identify risks, allocate resources wisely, and communicate status and direction effectively.&lt;/p&gt;
&lt;p&gt;Planning in SRE should not be rigid or bureaucratic. Instead, it should be lightweight, collaborative, and iterative, focusing on key aspects such as solution design, implementation, release, documentation, and monitoring - while remaining adaptable to change.&lt;/p&gt;
&lt;h2 id=&quot;best-practices-for-planning-sre-projects-and-tasks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#best-practices-for-planning-sre-projects-and-tasks&quot; aria-label=&quot;best practices for planning sre projects and tasks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Best practices for planning SRE projects and tasks&lt;/h2&gt;
&lt;p&gt;Below are the key principles and practices we follow when planning SRE work. These are not rigid steps, but rather guiding patterns that we adjust based on the nature and complexity of the task or project at hand.&lt;/p&gt;
&lt;h3 id=&quot;start-by-defining-clear-objectives&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#start-by-defining-clear-objectives&quot; aria-label=&quot;start by defining clear objectives permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Start by defining clear objectives&lt;/h3&gt;
&lt;p&gt;Before any planning activity begins, it&apos;s critical to define the objectives of the project or task. This includes identifying the desired outcomes, success criteria, and key performance indicators (KPIs) that will be used to measure progress and results.&lt;/p&gt;
&lt;p&gt;For example, an SRE project might aim to improve service reliability by reducing the number of incidents and outages by 50% over the next quarter, using metrics like mean time to recovery (MTTR) and mean time between failures (MTBF). Another objective might be to reduce the time required to roll out high-risk changes - such as operating system patches, Docker image updates, or a Java version migration - by streamlining the deployment process and redefining the rollout strategy.&lt;/p&gt;
&lt;p&gt;Clear objectives create alignment among team members and stakeholders, and serve as a north star when prioritizing work and making trade-offs.&lt;/p&gt;
&lt;h3 id=&quot;use-lightweight-tooling-to-visualize-the-plan&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#use-lightweight-tooling-to-visualize-the-plan&quot; aria-label=&quot;use lightweight tooling to visualize the plan permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Use lightweight tooling to visualize the plan&lt;/h3&gt;
&lt;p&gt;Planning doesn&apos;t require heavyweight tools or complex processes. In many cases, the best results come from simple, visual approaches that help teams collaborate and stay aligned. This might include physical whiteboards and sticky notes, or digital tools such as Miro, Mural, or shared spreadsheets.&lt;/p&gt;
&lt;p&gt;The main goal is to visualize the project as a series of interconnected tasks over time. This makes it easier to identify risks, dependencies, decision points, and resource requirements. A visual plan encourages discussion, feedback, and iterative improvements throughout the lifecycle of the project.&lt;/p&gt;
&lt;h3 id=&quot;embrace-the-shift-left-approach&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#embrace-the-shift-left-approach&quot; aria-label=&quot;embrace the shift left approach permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Embrace the shift-left approach&lt;/h3&gt;
&lt;p&gt;One of the most effective planning techniques in SRE work is to adopt a shift-left mindset. This means addressing risks, dependencies, decisions, and technical uncertainties as early as possible in the planning process, rather than waiting until they become blockers later in the project.&lt;/p&gt;
&lt;p&gt;For example, moving rate-limiting logic from an application cotainer to a sidecar proxy service may require architectural decisions, design reviews, and cost justifications. Addressing these questions at the beginning of the project ensures that the right people are involved, and that there&apos;s enough time allocated for approvals, discussions, and iteration.&lt;/p&gt;
&lt;p&gt;Where appropriate, teams can use Requests for Comments (RFCs), Architecture Decision Records (ADRs), or similar documentation formats to capture context, outline options, and document the reasoning behind key decisions.&lt;/p&gt;
&lt;h3 id=&quot;break-down-the-work-into-milestones&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#break-down-the-work-into-milestones&quot; aria-label=&quot;break down the work into milestones permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Break down the work into milestones&lt;/h3&gt;
&lt;p&gt;Rather than trying to plan an entire project in one go, it&apos;s more effective to break it down into smaller, manageable chunks - each with a clear milestone and deliverables. These milestones help track progress, provide natural feedback loops, and create opportunities to validate assumptions along the way.&lt;/p&gt;
&lt;p&gt;Each milestone can focus on a specific area such as design and architecture, implementation and testing, rollout and release, or monitoring and troubleshooting. However, it’s important to address all these areas from the start and to iterate over them continuously, rather than treating them as linear or isolated phases.&lt;/p&gt;
&lt;p&gt;For example, a project milestone might involve completing the rollout of an internal CLI tool used by SREs, followed by a demo and collection of feedback. The next milestone could involve improving the monitoring setup or optimizing performance based on feedback and real-world usage.&lt;/p&gt;
&lt;h3 id=&quot;identify-and-focus-on-the-critical-path&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#identify-and-focus-on-the-critical-path&quot; aria-label=&quot;identify and focus on the critical path permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Identify and focus on the critical path&lt;/h3&gt;
&lt;p&gt;The critical path refers to the sequence of tasks that determines the minimum amount of time required to complete the project. Identifying this path allows the team to focus on high-impact tasks, allocate resources strategically, and make decisions that directly influence project success.&lt;/p&gt;
&lt;p&gt;For example, in a platform migration project, the critical path might involve migrating only the most heavily used SRE commands first, while deferring less critical ones to later milestones. This approach allows teams to deliver value early, reduce risk, and avoid bottlenecks.&lt;/p&gt;
&lt;h3 id=&quot;estimate-work-and-verify-team-capacity&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#estimate-work-and-verify-team-capacity&quot; aria-label=&quot;estimate work and verify team capacity permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Estimate work and verify team capacity&lt;/h3&gt;
&lt;p&gt;To create a realistic plan, it&apos;s important to estimate the size and complexity of individual tasks. This can be done using simple methods like t-shirt sizing (S, M, L, XL), man-day estimates (1, 2, 3, 5, 8, 13 days), or story points - depending on what the team is comfortable with.&lt;/p&gt;
&lt;p&gt;Once estimates are in place, the next step is to verify the team’s capacity. This includes accounting for holidays, planned leave, ongoing operational responsibilities (like on-call rotations or incident response), and other project commitments. Each team member should declare their expected availability, and if the total capacity falls short of what&apos;s needed, then the plan should be adjusted accordingly - by reducing scope, extending the timeline, or increasing team size.&lt;/p&gt;
&lt;h3 id=&quot;communicate-the-plan-and-iterate-often&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#communicate-the-plan-and-iterate-often&quot; aria-label=&quot;communicate the plan and iterate often permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Communicate the plan and iterate often&lt;/h3&gt;
&lt;p&gt;Once the plan is drafted, it&apos;s important to communicate it clearly and transparently with all stakeholders. This includes sharing the objectives, timelines, milestones, risks, dependencies, and assumptions. Visual plans should include legends, clear labels, and dependency arrows to make them easy to interpret. The more visual and accessible the plan is, the easier it will be for everyone to understand and engage with it - e.g you can use four-quadrant influence/interest matrix to identify key stakeholders and their level of involvement (stakeholder mapping).&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 400px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/d861639fba6618dd657efc2c3de3172e/e17e5/planning-stakeholder-mapping.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 96.6153846153846%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAAsTAAALEwEAmpwYAAADcElEQVR42q2U7U9bdRTH90cZE2OyOaWoI8aHSNhgq2VucYnz4YV7Nc3cBm5BJfhC50OGumiGMDHRRU1MHIyyDmhAKvQB2tJBS3t77+3z072l9+PpHW5D+mIvPMnp+fV3ez79nnN+v7uHhzKLh7U9zQ9d18UzstqC7DioX2Kpl+1IZlT2DdB0GB/HGhvDunYNa2QEbt9uDUwkNkgks5LnxQo+jrWyF8L7JIr7H4WGJI5dh+efgyNHoKcHDh2CY8fkDzPbRVj3gZqWRstUoXwTog4yix1EJhzk/u4Q8JNYxg0Y+RnlpRcIdHezIdDQwYNYJ05AKrUbmM3qd4HVW5gBhw0L/NEmsZ3SX09IJ6Zg9Dqlrk5WRF1AYKGuLjh5EtLp3cCMrkrJOQFOYfr3kllwkPeJ0oV2aouPQV0Ufv8T5ssvojudbEq5uijluJSsKLuBuqaSTGnSqwRbd47Cuvx4/TDc6aGx5pQZr8sAvPDG63Dq1F1/+y0YuCh9r+04CTawUCiQz+ftja2tIoaRwjBViYr9/Z7ls1iaQkNP2/E+7D9TTqtpNuObFCgzpPxIv3KVC8oP9KeuMpgaRZF94gs0fnkTfnvHdkvW1tSgKKi3AErJ2ZTOghGmO3gG19JZXIFz9AbO0x16D7cRAc9XmJ88QukzB+VPn8K8JNO//Iyo3tzdQ0UUZpIavnoUl+8czhunOTp7FufEaRvuMaMw+w31L/YTGzqAr7+N/OcH4LtOrFbAZslqXMFnRngtMsCrnvd5ZeJdXOLHVz/gliFA7xVSg/vwDzzL8sWnWexzUL/SKQNoAczlc9QKFRarYZyr5+kN9uES7w30cXjlDB5jDWaGqQw1S26neqlNyt5PY7gDq5Dcnu8DQFVV0VUNU+7yeP4mw9lf+Tb7O19LHM39SdG+4zEs98fg/lD8I5i8AMvjradcLBYpluS4VGtUc2WMUhWzYlArVjDLBiV5XpZnpVqdksSyUbfX+YopYjSq1epO4L+WkYseT8RR5DqFI2GCoSCRtSihlRXiGxvi64TDq6xFo/JCiZNKJgmFQvfO8C7g/2E7gNb2pCKRCG63m5mZGebm5uw4OTlJVJRNT08zOzuL1+vF4/HYew/mtlTYfOEuLy/b5TQTmu73++3hxWIx5ufnWVpaIhgMSgvCdk6jYdnQfwDjqEYB/yPUowAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Planning: stakeholder mapping&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/d861639fba6618dd657efc2c3de3172e/e17e5/planning-stakeholder-mapping.png&quot;
        srcset=&quot;/static/d861639fba6618dd657efc2c3de3172e/ff46a/planning-stakeholder-mapping.png 325w,
/static/d861639fba6618dd657efc2c3de3172e/e17e5/planning-stakeholder-mapping.png 400w&quot;
        sizes=&quot;(max-width: 400px) 100vw, 400px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 400px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/281ffe398b00931cee8076d6a7596024/e17e5/planning-stakeholder-mapping-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 96.6153846153846%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAAPoAAAD6AG1e1JrAAAEOklEQVR42q2TeUzbZRjHn19dWVmhZHXF1nIsnC3ndIZ5LHboyDImUI5yquD+8CAu2yiWq01DYUFgU4HSYxV6H/zGamFh5RpX5Vg22aJ4/G9i4jL/MNEx6PH6dkGjaf/QxCd58ry/3/u+n+f7PL/nB/AvDAEQ8F+MJEnu9etuDimCiGkltM6piKF5NXFlbpgYcg+CbBEBTQ1w2EDAZRNBfIb9CvZhPUBDWKDTaeSTi4h98xMoumcn0F0LEbhjhgCO/i2SQNOTILQAyJcpgCYJwjsJEJjCwp0AvwcTBRlyAMpfQLv+08O9LhQ9q4Sa1VFAS6MHd1csSb6lEcbObT2ghRuUOiPApVl2LJrg8R47uVyvKy0t4CCIbTNAegiwR34hSdT2E2t2CERfGiKQx5Lk9Vh5PgzdvW2mI/c1qDEAKNwMOnKlpOxMpKf7JlJTkQ0rHAVIDQHK5R/y3m2+lezRguiOjYpWjDF+j4GBPMaDvvv2/WjBBXUOrHCeRkVOJtPr4nACLiYTjQPsYOVpIUCF9KP0pjYtb3IQeDNqyo/zKvhlTg0Pbqnh4cww8cAyRc3GSmptFHhkBfg56Fjdr1YC1nsBokMmQSKRxInF4sTgulnOYje3s46I257JapGyctvbD3H+PNfFguQeLjW3P46e1cel5gwwgbG39c+xkkilmZelPc+BlseJ1xQssTWvf8PWFGyy1QVfJ6hPrgEpSlg+A6fc1cS3N6vgnrsSNqerie+mqwibXAC0EKhEIeWPt44ksa4er+A7hSjbIfRn2ktQlr3El+EqRhRLSe0P5dC99d4+tPYO3bvacCCwXk9Fq28SO1PCvR7K/9ZDsbwti7yoTIkZyKvItZWh/LF6b56l0p8/9vZujl2IKLrTtZtF0LVe/xRa+iBxZ6Up27dQz0AzIth2VkWEAi9IJZlW/FGYypcqUoyn0cu2msf5jvrd/LGG7XRzIaJ+fqbuqzdAsdHIQsvnMx4tn8/E0BzfXM2+38Iq7OjqiH94boPBVb9WkUYWIb692M+3FQcy7CV+vhOXbBBW3i/FJb9P2yuZFtg4ewB56ijb42URoXNosVgS52/Mc0EMdK7qxOCzmhNOjlrg4GgE4wkqgQbMdYylEsh2i8AxXQbkTDmO5TAxVQZtYf9lo9H4tFarPbRGrkUiE6IjEkUuNiqj7raQMVuNZFS/vD+25WMy5mzv99HnzBuMi2soMuhyhGh2+xfxiyQZFRY8cHUgbtio4w9otckymaxAoVAUtnZ0nOzs7j7VKZMd62hpOo5jwaXOTkFfX/cRk06XotfrXzGZTLFh5/F/N4TQk8bqdLo83IZanLl8ZGSk1Gw2l+I+v6VUKo/hdU3w2Wq1FmF11Tge3bsbVt2Tl7ifCfigwOFwvGgwGI5iwPN4/apKpUrCSXLxXiFOkB8s12azvRC8IxKJIlgsVtQfGe/kmEAKxJIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Planning: stakeholder mapping&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/281ffe398b00931cee8076d6a7596024/e17e5/planning-stakeholder-mapping-dark.png&quot;
        srcset=&quot;/static/281ffe398b00931cee8076d6a7596024/ff46a/planning-stakeholder-mapping-dark.png 325w,
/static/281ffe398b00931cee8076d6a7596024/e17e5/planning-stakeholder-mapping-dark.png 400w&quot;
        sizes=&quot;(max-width: 400px) 100vw, 400px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Most importantly, remember that the plan is a living document. It should be reviewed and updated regularly as the project evolves, new information becomes available, or team capacity changes. A good plan encourages collaboration, facilitates alignment, and supports continuous improvement.&lt;/p&gt;
&lt;h2 id=&quot;a-step-by-step-example-visual-planning-in-action&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#a-step-by-step-example-visual-planning-in-action&quot; aria-label=&quot;a step by step example visual planning in action permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;A step-by-step example: visual planning in action&lt;/h2&gt;
&lt;p&gt;To make the planning process more tangible, let&apos;s walk through a practical example using a visual approach. We recommend using a digital whiteboard tool like Miro or Mural, which allows teams to collaborate asynchronously and create a shared visual plan. Below, we outline a six-step planning method with concrete tips, inspired by real-world SRE projects.&lt;/p&gt;
&lt;h3 id=&quot;step-1-visualize-the-project-timeline&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-1-visualize-the-project-timeline&quot; aria-label=&quot;step 1 visualize the project timeline permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 1: visualize the project timeline&lt;/h3&gt;
&lt;p&gt;Start by creating a visual board to lay out the project&apos;s timeline and task structure.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use a vertical axis to represent &lt;strong&gt;time&lt;/strong&gt; - this could be weeks, months, or quarters. Mark important dates, such as deadlines, releases, holidays, or other constraints that affect team availability.&lt;/li&gt;
&lt;li&gt;Use a horizontal axis to list &lt;strong&gt;tasks&lt;/strong&gt;. Represent each task with a sticky note that includes:
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;short, descriptive title&lt;/strong&gt; (e.g., “Add SLO dashboards”)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;color code&lt;/strong&gt; to distinguish task types:
&lt;ul&gt;
&lt;li&gt;🔴 Red for risks&lt;/li&gt;
&lt;li&gt;🟡 Yellow for planned tasks&lt;/li&gt;
&lt;li&gt;🟢 Green for resources or capacity&lt;/li&gt;
&lt;li&gt;🔵 Blue for decisions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Keep sticky notes &lt;strong&gt;small&lt;/strong&gt; to reduce visual clutter and force concise descriptions.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Draw arrows to indicate &lt;strong&gt;task dependencies&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;Solid arrows for confirmed dependencies&lt;/li&gt;
&lt;li&gt;Dotted arrows for uncertain ones&lt;/li&gt;
&lt;li&gt;Ensure arrows are unidirectional and avoid loops&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;legend&lt;/strong&gt; to the board to explain color codes, arrow styles, note sizes, and any other symbols used.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This setup helps the team grasp the big picture and start discussions around sequencing and priorities.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/e48917ad6bdbf6748b7a20db3bfb56cd/3fdca/planning-example-step-1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA+klEQVR42oWRuU7EMBBA8///wT/Q0qQCiWYFBSDBBlCijbSOzznNOFkO7RZM4TyP/eyJp0spLcvivWcL5Uip/htayjxl0G6baVX7PMfhargWKqp8yqv+Km0uBmP+eB0eAGq3bdmWWTgz+WMPZb/mzuVqsggwuxgIsTstSGIciYQJCF6YkSlCeWepTE7o8C1rFTZARGbptiTjFI69yYguHG+IFPJnWnrAmsNTiferrJv8U0u7mZ0Lt3cGh4Ue9/Hvj1yUfSGneXa7HTNNs38bPRNaC3LOVpuNxgYA0KAUDD7GaJkm29nWI7FRpMEavMYZUHsSew00aG1V/QIaH9Pyz7c/4wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Planning step 1: visualize the project timeline&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/e48917ad6bdbf6748b7a20db3bfb56cd/3c492/planning-example-step-1.png&quot;
        srcset=&quot;/static/e48917ad6bdbf6748b7a20db3bfb56cd/ff46a/planning-example-step-1.png 325w,
/static/e48917ad6bdbf6748b7a20db3bfb56cd/a6d36/planning-example-step-1.png 650w,
/static/e48917ad6bdbf6748b7a20db3bfb56cd/3c492/planning-example-step-1.png 1300w,
/static/e48917ad6bdbf6748b7a20db3bfb56cd/d544a/planning-example-step-1.png 1950w,
/static/e48917ad6bdbf6748b7a20db3bfb56cd/d2433/planning-example-step-1.png 2600w,
/static/e48917ad6bdbf6748b7a20db3bfb56cd/3fdca/planning-example-step-1.png 3111w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/8a481b9ac90bf21302571d3196f5f6cc/3fdca/planning-example-step-1-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAPoAAAD6AG1e1JrAAABpUlEQVR42o1Ry0rDQBSdpO+maYOPVt2IrVSoUJTgg1rIQhQ3PhbN2o1GLU59xLYpIrMWDSK6iRYFs8vGrrrND7jof/gR49zWVLEbB27umXsu5547QZ1OR3BdV/oOv2zJAeV9V0L/Oxx8rDpK7M6nV6O58tgPRQgPafZlu5B/3nyy8WIc4+kQ1ChFHMS3QD9UFfmA37pbWtouyVgUjeHuFNIT605TiBJGRIsaJyPXtUpyrTcLAc//ccZRVnMR8ivkNdyinyL9sAJ92/qhkDT0+ILjUN8jUWI6jpTK5VyMkHTy6mJ8BXou9dRU40LI9zZCPGGCmowClFLONM0IM+b3piO9El+oV0ZuAD9cz0xUsdQEXDufKNSwdAu4cTpWqh6LVcCqqvrAocUEPVP9YyQS6cZoygAszu0XkssHZ7/e1WseWNlzOCBIisVsPZPZA1xUDTmrHO2wNXjbtuOO48Ta7XYIcqvVFAFblhW9Z9ybrguOuj4EfVBHvT9IOdbQfQd4A5cFpYQHzCIIHBPzQYY71DVNCxDHCVosm+pyhPFBvIFDk5NK+Au+MJcOkFCHzgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Planning step 1: visualize the project timeline&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/8a481b9ac90bf21302571d3196f5f6cc/3c492/planning-example-step-1-dark.png&quot;
        srcset=&quot;/static/8a481b9ac90bf21302571d3196f5f6cc/ff46a/planning-example-step-1-dark.png 325w,
/static/8a481b9ac90bf21302571d3196f5f6cc/a6d36/planning-example-step-1-dark.png 650w,
/static/8a481b9ac90bf21302571d3196f5f6cc/3c492/planning-example-step-1-dark.png 1300w,
/static/8a481b9ac90bf21302571d3196f5f6cc/d544a/planning-example-step-1-dark.png 1950w,
/static/8a481b9ac90bf21302571d3196f5f6cc/d2433/planning-example-step-1-dark.png 2600w,
/static/8a481b9ac90bf21302571d3196f5f6cc/3fdca/planning-example-step-1-dark.png 3111w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;step-2-apply-the-shift-left-mindset&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-2-apply-the-shift-left-mindset&quot; aria-label=&quot;step 2 apply the shift left mindset permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 2: apply the shift-left mindset&lt;/h3&gt;
&lt;p&gt;Bring uncertainty, risk, and decision-making to the front of the planning process.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Identify what’s &lt;strong&gt;known vs unknown&lt;/strong&gt;, and mark the unknowns clearly on the board.&lt;/li&gt;
&lt;li&gt;Highlight &lt;strong&gt;risks&lt;/strong&gt; with red sticky notes, and specify which teams or systems they affect.&lt;/li&gt;
&lt;li&gt;Shift all risks and major decisions to the &lt;strong&gt;earliest possible point&lt;/strong&gt; on the timeline, so they don’t block downstream tasks.&lt;/li&gt;
&lt;li&gt;Estimate the &lt;strong&gt;time and effort needed for decisions&lt;/strong&gt;, and add them as tasks - don’t assume decisions “just happen.”&lt;/li&gt;
&lt;li&gt;For complex decisions, plan to write &lt;strong&gt;RFCs or ADRs&lt;/strong&gt; early. Use them to explore options and involve stakeholders.&lt;/li&gt;
&lt;li&gt;If documentation will be required (design docs, runbooks, training material, etc.), add a task and allocate time for it early in the plan.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This approach surfaces problems early, reduces surprises, and enables smoother delivery.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/f85f7f21dedd4de189ace59c6c6e6c4f/3fdca/planning-example-step-2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAABSklEQVR42m2Q22oCMRCGff+H6AP0plBvW6heiIgKKt2lreKKWt0WdzenyXGSTvaiUOgfyAyTfJn8MwAAJngE/1zP5m2ZUlLqkAJjQU/YJsX0j6LRty9t44Byj5gw7eC66zZO7kCu0RwbJ55uE23N8Di62S4jWUhJDefDsbQ2Zbju3OyDUwLi/XZ9sDYoiIpNo62Uj6PvhQiQ4byoDdoQmJLeuQwHIU3TCjZm7UawvTHUD7hokz298cV9/UKURdd/mOBAVpxzIWCGbVHI9ZobCUrFmLT11aUDjSALLcvk0+pS3u0fezbDv957z21rl4tVpc+NzwMzuNyygMlD8cmnc7Y1zvCgfjv/gXXTNJvXw5VzoYwx9CWjs5L3O1YNT+PogrdOATg6lUIpRXcyTBOk9zrlF1seeiGi74UYyWCkHZHquULBOUooxhh/ALAUyPZZ+fxgAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Planning step 1: apply the shift-left mindset&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/f85f7f21dedd4de189ace59c6c6e6c4f/3c492/planning-example-step-2.png&quot;
        srcset=&quot;/static/f85f7f21dedd4de189ace59c6c6e6c4f/ff46a/planning-example-step-2.png 325w,
/static/f85f7f21dedd4de189ace59c6c6e6c4f/a6d36/planning-example-step-2.png 650w,
/static/f85f7f21dedd4de189ace59c6c6e6c4f/3c492/planning-example-step-2.png 1300w,
/static/f85f7f21dedd4de189ace59c6c6e6c4f/d544a/planning-example-step-2.png 1950w,
/static/f85f7f21dedd4de189ace59c6c6e6c4f/d2433/planning-example-step-2.png 2600w,
/static/f85f7f21dedd4de189ace59c6c6e6c4f/3fdca/planning-example-step-2.png 3111w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/797762b81f07645967c8fd0939b17332/3fdca/planning-example-step-2-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAPoAAAD6AG1e1JrAAACEUlEQVR42mPYv38/z5ZLWwQZgMB4fkiu9mz/cBC7slLRtTmXXZlhdzm/+hyvBAbsgBFEzCxn4I81UHTh0sqSAItKGqdxMTCkcUkuCDUObDFO6cwX8yvJFyqozuRzk1kQKq071bmhs7OT13J+eK3hnCB5sKb6eqbQUAZmENOv39w8MMQ4l4GnSAQsJ2KZbydqkTkTxG4tkw5pqJDc8v//fyYQvypXuLUmjS0AxDac6ZujNtNeBGbgfwYGpv0MDCz29fM5Nvx/zfv/zExWsFyukLRMkY6xRmWp6MTKEuXUmkJNr1WrpvAADWWpbk6Sb0zndvVp0S5VmevXDQ6amT5cIO/WAw1MM2Zg/f+fgbG3t5Czvr6eBWxgBT9/WoWkZE7h5hbpgrRoSZCYTVSFoL5nPthl5UUiSWV5IskgdvDCDB+juYGLQOxVQC/PBBoIC0s4KJKU1GgU4i3gN8xs5dVN9QOJCRqnyYmZp0wGezubJy2+TKZDYUWse259NJ9sp5UU2NdQF2IY2Odso1SorJWj7lrgm1XfJXH//n6Obdu2se/cuYh70c6d3Pfj7TlcJvg4m84ObQAGA+OVVVfYJi5ezLeouJh7Vaib0GIgG6QeGrb14MCXsMvXFDXP7AcmI5aZM2eyrlq1ihkoxwak2err/zP9B2GgYaBwSgPK1wPFZ6alsfaGWnCC1OR65LLLy9tzAABi3rBiENvzKQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Planning step 1: apply the shift-left mindset&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/797762b81f07645967c8fd0939b17332/3c492/planning-example-step-2-dark.png&quot;
        srcset=&quot;/static/797762b81f07645967c8fd0939b17332/ff46a/planning-example-step-2-dark.png 325w,
/static/797762b81f07645967c8fd0939b17332/a6d36/planning-example-step-2-dark.png 650w,
/static/797762b81f07645967c8fd0939b17332/3c492/planning-example-step-2-dark.png 1300w,
/static/797762b81f07645967c8fd0939b17332/d544a/planning-example-step-2-dark.png 1950w,
/static/797762b81f07645967c8fd0939b17332/d2433/planning-example-step-2-dark.png 2600w,
/static/797762b81f07645967c8fd0939b17332/3fdca/planning-example-step-2-dark.png 3111w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;step-3-break-work-into-milestones-and-plan-rollouts&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-3-break-work-into-milestones-and-plan-rollouts&quot; aria-label=&quot;step 3 break work into milestones and plan rollouts permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 3: break work into milestones and plan rollouts&lt;/h3&gt;
&lt;p&gt;Group tasks into meaningful milestones with clear deliverables.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Define each &lt;strong&gt;milestone&lt;/strong&gt; by its outcomes - this could be a feature rollout, an internal demo, or a proof of concept.&lt;/li&gt;
&lt;li&gt;If a milestone ends with a &lt;strong&gt;production rollout&lt;/strong&gt;, plan extra buffer for coordination and risk mitigation.&lt;/li&gt;
&lt;li&gt;Choose between &lt;strong&gt;time-boxed&lt;/strong&gt; (e.g., 2 weeks) or &lt;strong&gt;feature-boxed&lt;/strong&gt; (e.g., complete rate-limiting feature) milestones depending on complexity.&lt;/li&gt;
&lt;li&gt;For projects involving multiple teams, use milestones to align on &lt;strong&gt;contracts&lt;/strong&gt; or deliver &lt;strong&gt;mocked APIs&lt;/strong&gt; to unblock dependent work.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;feature flags&lt;/strong&gt; or &lt;strong&gt;shadow mode&lt;/strong&gt; deployments to gather feedback early without exposing risk to users.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This ensures that each milestone delivers value and provides a feedback loop before moving forward.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/e885b4ebfe2d9a73d7d13d3cfafcaf5b/3fdca/planning-example-step-3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAABbUlEQVR42m1Q207CQBDt//+FTz74YqKJmmh8QFQM4VZKCwstLb1QeoW2u93uzQEefHEyyZzZmbM5ZzSMcZyd/DT3TgfKpVLnhOBCQCfUfyHbNk8IlZqQcpPLcYpv0EzfpVHm9Jb1cl+Fif48ybcZOy/LC+XyK4CkDQa6TlulQTOLu89N3UPiC/nIH76uWjel89gehFvaXVmKckEYJ5QpKXDHvbLFlGkwsZeRYa9XMTL9cBv+vBk4zJhbbq3M/47NF2edHdW+SKbu4nGKwQ7F2DWsMkk1cLUbTJBhv6Nq6iXLILhdeMYhBbtlTfRocDc6zIOiFyzWh8orOs65lII1tWCdxpXauFl/HPVdZibswyIP7sqpCpCaVzVKho/zwtyXqLbV1b34O6ImhdiV1IwaJyVB3hxr3GDeYNp1bdWIJ2d0by3SkknOMCGMUoYbAoCxC1kqGMAZIAVnIEmpMwB54voAVQpogQAF1AK4iJe/jnnAWjpguawAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Planning step 3: break work into milestones and plan rollouts&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/e885b4ebfe2d9a73d7d13d3cfafcaf5b/3c492/planning-example-step-3.png&quot;
        srcset=&quot;/static/e885b4ebfe2d9a73d7d13d3cfafcaf5b/ff46a/planning-example-step-3.png 325w,
/static/e885b4ebfe2d9a73d7d13d3cfafcaf5b/a6d36/planning-example-step-3.png 650w,
/static/e885b4ebfe2d9a73d7d13d3cfafcaf5b/3c492/planning-example-step-3.png 1300w,
/static/e885b4ebfe2d9a73d7d13d3cfafcaf5b/d544a/planning-example-step-3.png 1950w,
/static/e885b4ebfe2d9a73d7d13d3cfafcaf5b/d2433/planning-example-step-3.png 2600w,
/static/e885b4ebfe2d9a73d7d13d3cfafcaf5b/3fdca/planning-example-step-3.png 3111w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/b12308eec24fd9a8a9fa47923c4b0fc7/3fdca/planning-example-step-3-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAPoAAAD6AG1e1JrAAACP0lEQVR42mNYvHgxn4FLrJeBX1SoerKPL5uKjRYDFNjb27MoGbvw8+vbCzCoeLAzIAAjEmboz2cQiNGSc+KSS5MEqvNgZ1a2v8mm5/hfOCzwv5SZ+0ULD6NlAubp64VN/GdZuWo9EzOL+s+hH1oOMSuUGWZYfT0DE0gkbKaxkWNg4HRehgphBgZ5eQ5Wba8bPHr+/9k0g/8r2Pq8sPEx+s5uGPmfX8d7ik60R7NscMA8BkFvHZjrJAwt5QX1HbVFDLxV/zMwMAm7REjxOsUnSNgFaDIwAF2oIGd+U9PC6r+Wg/F/eXO7p05+Oq959CP/C2oFFT/5v05YNdY3VyHUYbeEn/9lTuXIAGUz0zUGHnb/uYzTnhj7+HCZKitbGiur/zZW0agAGjiR3UFO65q6qsl/HqPgPxLGLs8Vzay+8blH/Zd0D2wBOUnOyCHa1N/wv5Bd4n8p5/ha5QSf5aKGLtu4NN2mmpmZ8a0KZWCea2MguirUghNsoKSq1U1pbd//bLqB/1nVnZ5zaYZ8EvLy/C/gbF8DMlDNyjnc3F/rFZ9lzEdJp5gyl/2eJuAwBHq33p6BBRY5YJBmnMbKa+DRyW8SvJBPx32huLFHhb5XlIGYiq+FoX2MCkiNsJq7v1K421Mp38D3oup+iSCxzIp2wZlp9VyrQl3458yZw7tq1So2iIn//zP+B2KYBcjs/fvrWfbv389SX1/PYiw5k8vH2IcrLc2YNW3mTNYzZ86w7geKbwPGAYhdH1rPJi9vzwEArxapZjcOensAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Planning step 3: break work into milestones and plan rollouts&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/b12308eec24fd9a8a9fa47923c4b0fc7/3c492/planning-example-step-3-dark.png&quot;
        srcset=&quot;/static/b12308eec24fd9a8a9fa47923c4b0fc7/ff46a/planning-example-step-3-dark.png 325w,
/static/b12308eec24fd9a8a9fa47923c4b0fc7/a6d36/planning-example-step-3-dark.png 650w,
/static/b12308eec24fd9a8a9fa47923c4b0fc7/3c492/planning-example-step-3-dark.png 1300w,
/static/b12308eec24fd9a8a9fa47923c4b0fc7/d544a/planning-example-step-3-dark.png 1950w,
/static/b12308eec24fd9a8a9fa47923c4b0fc7/d2433/planning-example-step-3-dark.png 2600w,
/static/b12308eec24fd9a8a9fa47923c4b0fc7/3fdca/planning-example-step-3-dark.png 3111w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;step-4-identify-the-critical-path&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-4-identify-the-critical-path&quot; aria-label=&quot;step 4 identify the critical path permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 4: identify the critical path&lt;/h3&gt;
&lt;p&gt;Determine the essential sequence of tasks that governs project duration.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mark the &lt;strong&gt;critical path&lt;/strong&gt; on the visual board using a different color or style (e.g., mark relevant milestones in red background).&lt;/li&gt;
&lt;li&gt;Any risks, decisions, or dependencies on this path should receive high attention and priority.&lt;/li&gt;
&lt;li&gt;Try to identify the critical path early so the team can optimize for speed and reduce delays.&lt;/li&gt;
&lt;li&gt;That said, if the critical path is too risky or expensive, teams might &lt;strong&gt;intentionally defer&lt;/strong&gt; parts of it - for instance, migrating only a subset of critical CLI commands first.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This step helps the team focus energy where it matters most, and creates clarity for prioritization.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/67dda537e9495110618d11572a7edc3d/3fdca/planning-example-step-4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAABc0lEQVR42m1R2U7CUBDt//+HiQ8mGh+MS1RQEoNC2Yp0YSm1LS2lWLC9++JcE31yHm7OnJszOXPGQggdtvlnlmZVLCnSphSUkFIoLfV/pQjdF5gqSykpVwEPJtejm9ibbMrw2Wv8zVeYDS7t4mNv5FIrIzFDTVuQtDueUKItreR+sQpHXmTPZ87US/p3AVvs8LaqkoogpvSvGQ5SBRMkYjyCL8ot4LHdr8YtPL8/eG/Zx1N3mBdlrUhNj58M8XGZPCb+9qDjXXDvZDXRjOBw6lbFDsS6brfoy6M79Mv3AXY7r73u2egqXV4EL51eP/WL3ulwfuvmzmY2Wh8RhSUka2rJmQW2hGMvO3Y6dvn8PRiEw+lDuvVEkTTrRZnk0d5pe+X5zA+b2PiX4i84C/IiWZyvoiYK0SZGXwdxrFmDCeMS1mt4az05cfthxgSjjFKOGowx59yItbkK5GAC5VICFD9ACDiWeekPK6QAAfTgFgDwSqlvd869bbaOl3QAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Planning step 4: identify the critical path&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/67dda537e9495110618d11572a7edc3d/3c492/planning-example-step-4.png&quot;
        srcset=&quot;/static/67dda537e9495110618d11572a7edc3d/ff46a/planning-example-step-4.png 325w,
/static/67dda537e9495110618d11572a7edc3d/a6d36/planning-example-step-4.png 650w,
/static/67dda537e9495110618d11572a7edc3d/3c492/planning-example-step-4.png 1300w,
/static/67dda537e9495110618d11572a7edc3d/d544a/planning-example-step-4.png 1950w,
/static/67dda537e9495110618d11572a7edc3d/d2433/planning-example-step-4.png 2600w,
/static/67dda537e9495110618d11572a7edc3d/3fdca/planning-example-step-4.png 3111w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/d619e011abdb311fe5a6fd8ea349220a/3fdca/planning-example-step-4-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAPoAAAD6AG1e1JrAAACPUlEQVR42mPYsGEDb4KlkXu0hX6gZ5SBp7swmzoDFNjb27MImZnxMSi58DOoeLAzYAJGENGfzyAQoyXnxKWRJsngocLA7sjNctNBgOW/mYfef2dd5YvmbkbLBE3T1oua+M+y8tB+J24d959NK7AcbERoKDPIoP8gXM/ABBIKm2ls5BgYOJ2XoUKYIZ6BgcNCTOKmqazafzMx5f/WxkYvLH1NvrMaRv/nNfKdoevhFypoGpzBquOhB3cVyCDhJF5BQSV+kMFCTqHSvE4xCRJ2AZpgF/rycN0M0hD8H67H8d9HVfCpv4fca3l19//ySuYVd10Y+Ltd5RQ1RIyNxN0d1ktGeG9jkvctNnXVOyVtFfqZQb9K2lxZzthYWf23ibJ6FchG9jhhvmuhQmz/jZT1/rgqij0PVOL8aWmg898s3uZOpDv/V38Dzf+a6h7XTXy1f0h7Rf8XdUk8pO9vdVjU0P8Ev26QEsiQuR4GojN9jLkYVFRU2P35WW86yaj/t5SU/+/Ex/rcQEr/s42F/H9rc7FJbqLsfR6S/CtMZFSnWXhp3xKzCH0oGRZzzH59qAEkSLXYYJEDBmnGxqxuEnydNkrqC13FhRf6yApXRFnpG4QKClrmaRnL/f//H6xYyyxRS8nH8bN0uP9/Yc2gSSCx7uJi7lWhLvxz5szhXbVqFRvYwP//gbEF1QThI9j1//+z1NfXswEVM+fm5rLL6VYI8svZCKr7WYEN2F9fz7LNw4P9zJkzrPWh9Wzy8vYcAHpkqGaLczM6AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Planning step 4: identify the critical path&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/d619e011abdb311fe5a6fd8ea349220a/3c492/planning-example-step-4-dark.png&quot;
        srcset=&quot;/static/d619e011abdb311fe5a6fd8ea349220a/ff46a/planning-example-step-4-dark.png 325w,
/static/d619e011abdb311fe5a6fd8ea349220a/a6d36/planning-example-step-4-dark.png 650w,
/static/d619e011abdb311fe5a6fd8ea349220a/3c492/planning-example-step-4-dark.png 1300w,
/static/d619e011abdb311fe5a6fd8ea349220a/d544a/planning-example-step-4-dark.png 1950w,
/static/d619e011abdb311fe5a6fd8ea349220a/d2433/planning-example-step-4-dark.png 2600w,
/static/d619e011abdb311fe5a6fd8ea349220a/3fdca/planning-example-step-4-dark.png 3111w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;step-5-timebox-tasks-and-check-capacity&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-5-timebox-tasks-and-check-capacity&quot; aria-label=&quot;step 5 timebox tasks and check capacity permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 5: timebox tasks and check capacity&lt;/h3&gt;
&lt;p&gt;Ensure that planned work is realistic based on available team bandwidth.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;tags or labels&lt;/strong&gt; to estimate task size or complexity (e.g., S/M/L or 2, 3, 5 days).&lt;/li&gt;
&lt;li&gt;Ask each team member to &lt;strong&gt;declare availability&lt;/strong&gt; per week, excluding holidays, on-call time, and other commitments.&lt;/li&gt;
&lt;li&gt;Update availability regularly to reflect changes (e.g., new projects, vacations).&lt;/li&gt;
&lt;li&gt;If the plan doesn’t fit the available capacity, adjust by &lt;strong&gt;scoping down&lt;/strong&gt;, &lt;strong&gt;shifting timelines&lt;/strong&gt;, or &lt;strong&gt;adding contributors&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This practice protects the team from burnout and ensures that commitments are feasible.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/608f3db9d24c66aa5612d9802cf3923b/f581c/planning-example-step-5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.46153846153847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAABe0lEQVR42mWQW2+bQBCF+f//JZViqWn6YCWKkiipLbBxKRhwQsAYvEsWG7z3Wxc1bz3SSKPRfJozx2OMYdAOzb7oMkUGbaw1RmktFCfCSGX/lzF4PNaYGc8aLd9Stg1/BvP3eLOH2Twa43p4b4KZD0okp+1/zIRp10DWLuJEcDvB/a4owu1hlabR77T2HwpRIo5GgrDg2nyRxgjtjGi3T4WqenKh3HNzulwM4R3N5+ftr+PH/HXZtuCs6Ymgjo486IqbMqqRrWBys25GZiUj5Z/kDDsHW/z4QJ/v4/UOxWu8Xbz6L1eb783uevfytPTr4jOYbcofSRW2WdYQJqbjghItpedcyShIn1ZtGMksyvy3TXQPYa46wOqPUwNyuLqND7Mkr8nBXVJafX1irWe0pqDpqgrvSwwaRkaFqSCMSaWswUQ9lsm3fL0HQnLOGRMYE0KEEBPsinLRnwdMqFIK9acLoVJKhHoAO8rYJxrgEWFKL5cJcxlp7WKc9BfOSbvtmEvcKQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Planning step 5: timebox tasks and check capacity&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/608f3db9d24c66aa5612d9802cf3923b/3c492/planning-example-step-5.png&quot;
        srcset=&quot;/static/608f3db9d24c66aa5612d9802cf3923b/ff46a/planning-example-step-5.png 325w,
/static/608f3db9d24c66aa5612d9802cf3923b/a6d36/planning-example-step-5.png 650w,
/static/608f3db9d24c66aa5612d9802cf3923b/3c492/planning-example-step-5.png 1300w,
/static/608f3db9d24c66aa5612d9802cf3923b/d544a/planning-example-step-5.png 1950w,
/static/608f3db9d24c66aa5612d9802cf3923b/d2433/planning-example-step-5.png 2600w,
/static/608f3db9d24c66aa5612d9802cf3923b/f581c/planning-example-step-5.png 3072w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/1cb933298c08d2416b50d71fe89f1cb1/f581c/planning-example-step-5-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.46153846153847%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAPoAAAD6AG1e1JrAAACUklEQVR42mNYtGgRd6iKfFSApnymY5RuqpcorxUDFBgb+3AZ2mup8BoEqfLregsyYAJGEFFfzyAWbqgWzqGcKcvgocLA7sjHftNRkP2/mbPBfyd9zevmbka7BczSj4ka+S+z9tD9JGET/59DO6AabERoKDPIoP9ADDSICSw0z0LXLjS8jYGhg58hl4GB3UJM7JqpvNp/K1G5P9YGes8svY2/seqF/ufR8+xX9U424zMNcufQdJRnYPgPchELg5YWG6esn5SgkrXcf6ChgibhskJG3pVSJm5WDCpAFwbycN8MVuf/H6bH+d9XTfBZiKfMG2U19/8qamZ1zY4MylnmquaaYqbOkh7WRyUivM8yy/s1mbvrX5W2jfzHoN0sa6cko2uurP4aiItBLmaPFuS7FiLM+V9fyeCvs7zYMy953p8WRtr/zeKsbkW68n/119f4r6Xuds3EQ+O9pEP4H3GPuGOaAQ4bhXV9lgtq+8uCDJkfby8w396eA+hCFXY/ftabdvI6/20kZf4787M+NZI1fGtrKvvfzlCky1mYo9RHkq/HTE61w8Rb57W4XfBnSf+Yk+57/dQhERLKxvAfHBQQkGZszOolJ1zqpKLW5SEt0hWgIpWU6WYiGycqq1xv7yPyH6pY2jxBVcHH+bZ0mO9zYQ3/ZpBYZ2kp79Iob8HFixfzbdu2jR1uqEVoqJCjb7C2V2ioRGh9PZu9p5+JsZWjsqurK7eppaWzvb2zh5m9vYyxRZC1iYlzsIaJiY6zs7eSm5sb2LurVq1itre3Z5GXt+cAAPnsowTbkZJOAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Planning step 5: timebox tasks and check capacity&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/1cb933298c08d2416b50d71fe89f1cb1/3c492/planning-example-step-5-dark.png&quot;
        srcset=&quot;/static/1cb933298c08d2416b50d71fe89f1cb1/ff46a/planning-example-step-5-dark.png 325w,
/static/1cb933298c08d2416b50d71fe89f1cb1/a6d36/planning-example-step-5-dark.png 650w,
/static/1cb933298c08d2416b50d71fe89f1cb1/3c492/planning-example-step-5-dark.png 1300w,
/static/1cb933298c08d2416b50d71fe89f1cb1/d544a/planning-example-step-5-dark.png 1950w,
/static/1cb933298c08d2416b50d71fe89f1cb1/d2433/planning-example-step-5-dark.png 2600w,
/static/1cb933298c08d2416b50d71fe89f1cb1/f581c/planning-example-step-5-dark.png 3072w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;step-6-materialize-and-execute-the-first-milestone&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-6-materialize-and-execute-the-first-milestone&quot; aria-label=&quot;step 6 materialize and execute the first milestone permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 6: materialize and execute the first milestone&lt;/h3&gt;
&lt;p&gt;Once planning is complete, it’s time to operationalize the work.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Translate the first milestone into actionable tasks on your team’s sprint board (e.g., Jira, Trello).&lt;/li&gt;
&lt;li&gt;Link sticky notes on the whiteboard to tickets in the backlog to maintain traceability.&lt;/li&gt;
&lt;li&gt;Monitor milestone progress closely, and update the plan as the situation evolves.&lt;/li&gt;
&lt;li&gt;Share status updates with stakeholders frequently, and be transparent about changes in scope, risk, or timelines.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By grounding your planning in both a visual workspace and a sprint tool, you maintain alignment between vision and execution.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Planning in the context of Site Reliability Engineering is not about creating perfect schedules - it’s about building a shared understanding of goals, constraints, and strategies. It enables teams to stay focused and effective, even in the presence of uncertainty and operational disruptions.&lt;/p&gt;
&lt;p&gt;By using lightweight planning tools, applying shift-left practices, breaking work into milestones, identifying the critical path, and verifying capacity, SRE teams can stay resilient and adaptive. Most importantly, good planning makes work visible, promotes healthy team dynamics, and increases the chances of delivering reliable, scalable, and valuable outcomes.&lt;/p&gt;
&lt;p&gt;If your team is struggling to balance operations and engineering work, or if your projects frequently get derailed by unexpected events, it might be time to revisit your planning practices - and hopefully, the ideas shared here will serve as a helpful starting point.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Using generative AI as an architect buddy for creating architecture decision records]]></title><description><![CDATA[As software architects, we know how valuable Architecture Decision Records (ADRs) are for documenting critical decisions clearly. However…]]></description><link>https://handsonarchitects.com/blog/2025/using-generative-ai-as-architect-buddy-for-adrs/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2025/using-generative-ai-as-architect-buddy-for-adrs/</guid><pubDate>Mon, 31 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/15da0ad3f260ba0335f31c1448747e5a/2bef9/architect-ai-buddy.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFAklEQVR42nWT51NUZxTG71+QrxmTaMYkY6JOjJp8MCrYBaSKBEEQF4NRxHHEBlKCGqVIlCaKDQ1YkCLFFaU3gZUILmUAYelFqrBIWXaBX+5ewEk+5M6cmXee9znPe557zhGUje3EyAvIeF1PVkUDdV39qEfHmZmZZmpqSorp6amP5/nQ3w+JPGXrOyrEyK1UkVdehxD9XIH75Vhisl+jaOikTz3K9JzI/8XUlI6x8TF0Iq+tf5jitx1kKBtJLnqDkFJUiWdEHPnVLfSIYswlzMzMSFXMVzf9EZ9Gp9PR0dkJiBwR7x76QHaVinRFNcKz0mouRKdQ3z3A2IQGJBpoxkfRTk6KQvPWdUyJyZMiplaP0KhqkjCdTitlNPcMkq23nK6oITwhm66hUUbUauorFGTFReNuu4OqshLpgfmkkZEP9Pb10ds/QFNLG6rWDqla0RYa8aGGzl6EfGUDj/IqRAtdBJ04zEnzNRw3/pGzztao6mr/I3g17jluAbeJl2eSXVLO7v1HkWfkSpxx0V1n/xCCsrmTTKWK3OfPMF22EKOli4hw20V9SgTve7slst6aVqzE+qA3C75agY2jCy7uvnzxzUqCr96ROBOaOcFqseWFNc20qBr4+etFWHz7OQ/cbWl9epX+pirRDGi1k1JSUMg1DFZ9h0wmIywmlf1HTtPU2j5boV5wQBTsHhxGUdskgaV5OZw028Bdp62UBLqiiL8tNWXWMnT39rHWTMYmI3NCb8Ui2+vIo4cPxSapRQdaugaGETSTWiqbOqSE+vYe/A854rxiEZZLFlJekIN2Yuxf3YScghK8fM+h0Uzg5eOHz7kAcvILKStX0j+kRvgwKnZ3bAJlYxtbXXw54OTMlcOOJNx/gOrNK6qzktFMzG6OThTWz6P+ez80RM7LV8QkpOHsegIbJ1dyi8sQQu48Rp6vYLdXOPsCY1llcYD1pnYMj2lQv2vnhv0G6gtfzDZHrFJvTT+Xg4ODKBtaMLayY91GI5avXo/PpesIe4/64XD6T36wcMXGM4xtLl44/B6Jqq2T/p5uLtpupaP2zcfx0Wq10n8dGBigQPkW+99OsGWjIWvXGnAm6CaC+Z7D7Dx4hsVrLDF3D8bO5ypmp0KJSsjgQWom60wdJXvz46NfSf03JA75zcfP8AqM5HV1HdFPMjjqcwlh7TYbduz34NNlGzB08mSv/z2Wbd/HUsNdLF5njfMp/zkxLTqthsSkZNIzsimrqOTIaT8uhN2lRtWGf2QMHn4BCCt+2sT3BlYsWm3EEkMbth06j/vFSExtf2X5BnFbWtrRTc9I+60fj5TUNB7EJeHhe5Ho6Hs8fZ6FvbMrW0ysyM7JRdhksptPFq7ks1XGfGlgx4LVxmx3PS9av4b1sQCe5ZdQ+iicqqQoagsziE9MJiYuhYQnT2ls6aC8RsVmk52sXGdEXlEpQpjvcXabm2Fq7cghmRO7zEwxMbXCbs8+tlvai+HICUdb9lhY4GBphYmZDZuNrTnjJuNKoD/BwSF4Ov+CzGg9ZfL7CAURx0jxteWxzx7Sz9qRGSCjLPYPlPHBFP91gawob1JDTyOP8CQ90ht5uAc5IlafGkbmrfO8uO7Ny3A3cv0dUMb6IeQ+vIH8ehCJgSdJDvXjRXQYSSF+JIlneVQgmdGXyYuNoDQlhpqCdBr/LuBVWizlWak0KxWUpd2nKPEuJeK9qqKYfwBuNdLQJQhshgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;AI buddy&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/15da0ad3f260ba0335f31c1448747e5a/2bef9/architect-ai-buddy.png&quot;
        srcset=&quot;/static/15da0ad3f260ba0335f31c1448747e5a/ff46a/architect-ai-buddy.png 325w,
/static/15da0ad3f260ba0335f31c1448747e5a/a6d36/architect-ai-buddy.png 650w,
/static/15da0ad3f260ba0335f31c1448747e5a/2bef9/architect-ai-buddy.png 1024w&quot;
        sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As software architects, we know how valuable Architecture Decision Records (ADRs) are for documenting critical decisions clearly. However, we also understand that creating detailed ADRs can feel tedious and time-consuming. That&apos;s why we&apos;ve started using generative AI as a helpful assistant to streamline the ADR creation process.&lt;/p&gt;
&lt;p&gt;In this article, we’ll share our experiences with leveraging AI to efficiently generate high-quality Architecture Decision Records. We&apos;ll discuss practical techniques, provide examples, and outline the benefits and challenges we&apos;ve encountered.&lt;/p&gt;
&lt;h2 id=&quot;how-we-practically-use-ai-in-the-adr-process&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-we-practically-use-ai-in-the-adr-process&quot; aria-label=&quot;how we practically use ai in the adr process permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How we practically use AI in the ADR process&lt;/h2&gt;
&lt;p&gt;An effective ADR typically consists of three key sections:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Context&lt;/strong&gt;: Explains the background and the critical factors that led to the decision. Even if not every detail is captured, a well-described context helps future readers understand the reasoning behind our choices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Considered options&lt;/strong&gt;: Lists possible alternatives we evaluated. Sometimes, if there was clearly only one viable option, we combine this section with the context.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decision&lt;/strong&gt;: Clearly defines our selected solution, along with its rationale. We typically describe the expected consequences - what will become easier, what might get harder, and necessary adjustments for the team and architecture.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To effectively integrate generative AI into ADR creation, we&apos;ve adopted a structured, iterative approach that involves carefully constructed prompts.&lt;/p&gt;
&lt;p&gt;Since generative AI currently (checked on GPT 4o, 4.5, o1, o3; Claude 3.7 Sonet; Gemini 2) struggles with independently capturing accurate context, we always start by clearly providing that context ourselves. From there, we use iterative prompting with AI:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First, we provide a detailed context and ask AI to generate a few considered options, along with their pros and cons.&lt;/li&gt;
&lt;li&gt;After reviewing these options, we prompt the AI again, explicitly asking it to recommend a decision based on deeper analysis and project-specific factors.&lt;/li&gt;
&lt;li&gt;Once we select an option, we explicitly ask AI to outline its consequences - what will become easier, more difficult, and what adjustments may be necessary for the team and our architecture.&lt;/li&gt;
&lt;li&gt;Finally, we prompt AI to re-examine the decision thoroughly to make sure no better solution exists, ensuring we haven&apos;t overlooked important aspects.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This iterative approach helps us progressively refine the AI-generated content, ensuring clarity, accuracy, and practical applicability.&lt;/p&gt;
&lt;h2 id=&quot;example-prompts&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#example-prompts&quot; aria-label=&quot;example prompts permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Example prompts&lt;/h2&gt;
&lt;p&gt;Here are some practical examples of prompts we’ve successfully used when leveraging AI for ADR creation. First prompt (&lt;code class=&quot;language-text&quot;&gt;&amp;lt;base-prompt&gt;&lt;/code&gt;) to define context clearly:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;markdown&quot;&gt;&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;As an experienced software architect who specializes in modern software development, continuous delivery, and architecture trade-off analysis you were asked to prepare an architecture decision record.
You have an analytical approach to the topics you consider. You try to objectively see the advantages and disadvantages of each option so that you can choose the one that best suits the situation.

Given the following context (inside triple quotes):
&quot;&quot;&quot;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Your&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;detailed&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;here&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&quot;&quot;&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When additional architectural drivers or user stories are relevant, we add those details as follows:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;markdown&quot;&gt;&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;base-prompt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

Also, you consider the project in which you are working, the team, and the architecture drivers (inside triple quotes):
&quot;&quot;&quot;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;your&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;archiecture&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;drivers&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&quot;&quot;&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We recommend using a new session for each prompt to avoid AI using hallucinations from the previous session.&lt;/p&gt;
&lt;p&gt;Next, we ask for considered options clearly detailed with pros and cons:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;markdown&quot;&gt;&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;base-prompt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
Provide a list of 2 to 5 viable options with their pros and cons.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then we review the options and typically we have our own (human) brainstorming session to decide which option is the best. After reviewing these options, we typically ask the AI to further refine the recommendation:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;markdown&quot;&gt;&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;base-prompt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

The options you are considering are (inside triple quotes):
&quot;&quot;&quot;
&lt;span class=&quot;token list punctuation&quot;&gt;1.&lt;/span&gt; Option 1 with pros and cons
&lt;span class=&quot;token list punctuation&quot;&gt;2.&lt;/span&gt; Option 2 with pros and cons
&lt;span class=&quot;token list punctuation&quot;&gt;3.&lt;/span&gt; Option 3 with pros and cons
&quot;&quot;&quot;
For each option consider pros and cons. Next, look at the list of options and consider the most fitting option to a given context and write the decision in the following form:
‌&quot;&quot;&quot;
In the context of &amp;lt;use case/user story u&gt;, facing &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;concern&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; we decided for &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;, to achieve &amp;lt;system qualities/desired consequences&gt;, accepting &amp;lt;downside d/undesired consequences&gt;, because &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;additional&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rationale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;.
&quot;&quot;&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To explicitly address consequences, we ask:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;markdown&quot;&gt;&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;base-prompt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
We decided on &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; because (inside triple quotes):
&quot;&quot;&quot;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Decision&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rationale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&quot;&quot;&quot;
List the consequences clearly (what becomes easier, more difficult, and necessary adjustments) in the form:
&quot;&quot;&quot;
Easier:
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;bullet&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
More difficult:
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;bullet&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
Necessary adjustments:
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;bullet&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;points&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&quot;&quot;&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, to ensure thoroughness, we perform a final check:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;markdown&quot;&gt;&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;Look again at the context, considered options, the decision, and consequences. 
Confirm whether there&apos;s a better option (even new or combined) and state your final decision clearly, including the rationale.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The last prompt is a good way to ensure that you have considered all the options and their consequences before making the final decision. Be aware that 90% of the time AI will suggest some &quot;hybrid&quot; or &quot;combination&quot; of the previous options that needs to be ignored most of the time. But the important part of this check is the rationale behind the decision and the consequences of the decision. It sometimes shows the context aspects you forgot to mention in the context. And hence the iteration starts again.&lt;/p&gt;
&lt;h2 id=&quot;benefits&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#benefits&quot; aria-label=&quot;benefits permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Benefits&lt;/h2&gt;
&lt;p&gt;Generative AI has notably improved our productivity by rapidly creating initial ADR drafts, allowing us to focus more on strategic thinking rather than manual documentation, fixing grammar and spellings. It has also helped us maintain consistency and clarity in our documents, reducing misunderstandings across teams (avoiding ambiguity and mind shortcuts). Additionally, AI suggests improved wording and structure, enhancing readability and quality.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-limitations&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#challenges-and-limitations&quot; aria-label=&quot;challenges and limitations permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Challenges and limitations&lt;/h2&gt;
&lt;p&gt;While AI has been helpful, it’s not without challenges. The biggest limitation we&apos;ve observed is its difficulty capturing context accurately without detailed guidance from us. Quality greatly depends on the clarity and completeness of the information we provide initially. We also learned to watch carefully for inaccuracies or &quot;hallucinations,&quot; especially when AI generates pros and cons for each option. AI sometimes introduces incorrect or exaggerated details, so human validation remains essential.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;Generative AI has significantly improved how we create Architecture Decision Records, making the process faster and clearer, and helping ensure consistency across documents. However, AI isn&apos;t (yet?) a replacement for architect expertise. We must still carefully provide context, validate AI-generated information, and iteratively refine outputs. When combined with human judgment, AI serves as a powerful assistant that enhances our ADRs and makes the entire decision-making process smoother and more effective.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Food truck freedom: explaining service architecture to business stakeholders]]></title><description><![CDATA[Recently, our team embarked on a major architectural transformation - moving from a monolith to a services-based architecture. As expected…]]></description><link>https://handsonarchitects.com/blog/2025/explaining-service-architecture-to-business-stakeholders/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2025/explaining-service-architecture-to-business-stakeholders/</guid><pubDate>Sun, 16 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/da878bbb2ab501e89707c885aae0fe3f/fa60d/food-truck-festival.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.230769230769226%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAACf0lEQVR42gXBWW/SAAAA4P4dX41Pxlczl8UlzjndnDtlGzeltEBbWnpADyhHobQUCl3pZOVm7MiYuMyYRacmuizx7/h9wPt92BOMeEE4FI3hHCdWCvWu2Rq3yrIgbDzTNubPabS869PR7Ra+Y1FrVPAV6V5V4Q8UsQDUR45saoc98/bX8OFhcvfF+Hle7NnS1Ip9O1wUyYVB3V9W6SpHibYKmqWpuHlcCptFeCD7gft/08lAu2hlbx2+X4h2hIBGekSK+G4fNLGn6fDLwlR7XdeDGs1LIKSQ1zNS5VaH7LIVWwTG+XijRmVRtxrdqfnW1Xyi1JSjGDbpWDcn1SbvCtvKE1qAWERKeR09URTfpjcfH0ZeyOAKUCUOdAlpTmy1xHICpUhJ01JIAisJjJQRxCJ7eVbVC7CmM1OB/ZRW7DaVqbA9h2hIe0AB9pL7u7W2fnH/tT9qZ2kai+JwCAp7PCGvN0YHHUfuVaBNmeTgDSK43aqgNSOebyYyJRTICqRVwPs1ZnR3Nhgc10dtSynGIkgIBA/cQf/ucpv19fR4GWc5Cm4U/A63peCLBrJeTO4ADdKFrswx/q2RkSkdG107PR53axcdTaYZPADtz2kTUK/CvaDvh5NxKq7sx6Wm2/8XznfFKBAI+NKmdiTjs6TrrJ7MiagxG1z+ub76PWNifkYF42M4ZYBH+ehYrw7D/LkHuWESJ8vxfhICOrXUaVc39LQUeZMDV4lI6HSklyt5e9bnneZH6F0y5WrUkJmetZZ46VF48NwnoVCKJdpSHOhWWDu2dyRB+ud+QilKOHal4hoT7tK4OTIpfLuMrF3k4kNeUuJYPoEp1UzOFjgbgfn5/7vKZmfV2VSXAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Food truck festival as services architecture&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/da878bbb2ab501e89707c885aae0fe3f/3c492/food-truck-festival.png&quot;
        srcset=&quot;/static/da878bbb2ab501e89707c885aae0fe3f/ff46a/food-truck-festival.png 325w,
/static/da878bbb2ab501e89707c885aae0fe3f/a6d36/food-truck-festival.png 650w,
/static/da878bbb2ab501e89707c885aae0fe3f/3c492/food-truck-festival.png 1300w,
/static/da878bbb2ab501e89707c885aae0fe3f/fa60d/food-truck-festival.png 1792w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Recently, our team embarked on a major architectural transformation - moving from a monolith to a services-based architecture. As expected, the engineers on our team understood the rationale behind this shift, but the business stakeholders had concerns.&lt;/p&gt;
&lt;p&gt;They asked: &lt;em&gt;Why should we move away from a system we have already seen in action? What benefits do services bring beyond just technical improvements?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We needed an analogy that would make the differences between monolithic and service architectures clear, relatable, and memorable. So, when we were asked to present our reasoning to leadership, we came up with something that clicked immediately: comparing a large restaurant to a food truck festival.&lt;/p&gt;
&lt;h2 id=&quot;monolith-vs-services-restaurant-vs-food-trucks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#monolith-vs-services-restaurant-vs-food-trucks&quot; aria-label=&quot;monolith vs services restaurant vs food trucks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Monolith vs. services: restaurant vs. food trucks&lt;/h2&gt;
&lt;p&gt;Think of two contrasting dining experiences. In one, you have a single large, busy restaurant with one centralized kitchen-this represents monolithic architecture. The restaurant is responsible for preparing everything: burgers, vegan dishes, desserts, and more. All the chefs share the same kitchen space, use the same equipment, and rely on a single team to serve all customers. At first glance, this setup seems efficient because everything operates under one roof. However, let’s dive deeper.&lt;/p&gt;
&lt;p&gt;Now, imagine a large food festival with potentially thousands of customers-this represents service architecture. Each food truck in the festival is an independent service. Every truck specializes in a specific type of food: one serves burgers, another serves vegan dishes, and a third specializes in desserts. Each truck is self-contained, with its own staff, ingredients, and equipment.&lt;/p&gt;
&lt;p&gt;Now, let’s break it down into specific areas of comparison.&lt;/p&gt;
&lt;h2 id=&quot;specialization-and-autonomy&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#specialization-and-autonomy&quot; aria-label=&quot;specialization and autonomy permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Specialization and autonomy&lt;/h2&gt;
&lt;p&gt;Monolithic architecture (large restaurant): A single, large restaurant has one centralized kitchen where all food is prepared. If the kitchen becomes overwhelmed or disorganized, the entire operation slows down. The burger chef might have to wait for the dessert chef to finish using the stove, and the vegan chef might struggle to avoid cross-contamination. There’s little room for specialization because everyone depends on the same resources.&lt;/p&gt;
&lt;p&gt;Service architecture (food trucks): Each food truck focuses on its specialty without relying on others (often they even don’t know about each other). This independence allows them to work faster and better, with no conflicts over shared staff, ingredients, or equipment.&lt;/p&gt;
&lt;h2 id=&quot;scalability&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#scalability&quot; aria-label=&quot;scalability permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Scalability&lt;/h2&gt;
&lt;p&gt;Monolithic architecture (large restaurant): When more customers arrive, the only way to handle increased demand is to expand the kitchen, hire more chefs, and cram more equipment into the same space. Scaling becomes expensive, disruptive, and inefficient as the kitchen becomes overcrowded (which may result with poor customer experience like cold dishes).&lt;/p&gt;
&lt;p&gt;Service architecture (food trucks): If the festival grows, new trucks can join - just like new teams or services in software. This allows for more variety and the ability to serve more customers without disrupting existing operations.&lt;/p&gt;
&lt;h2 id=&quot;fault-tolerance&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fault-tolerance&quot; aria-label=&quot;fault tolerance permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Fault tolerance&lt;/h2&gt;
&lt;p&gt;Monolithic architecture (large restaurant): If one part of the kitchen fails - say, the stove breaks down or a chef calls in sick - the entire restaurant slows down or even stops. A single issue can ripple through the entire operation.&lt;/p&gt;
&lt;p&gt;Service architecture (food trucks): If one truck faces issues (runs out of supplies or has a technical failure), the others continue serving customers. One truck going down doesn’t shut down the whole festival.&lt;/p&gt;
&lt;h2 id=&quot;coordination-without-overlap&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#coordination-without-overlap&quot; aria-label=&quot;coordination without overlap permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Coordination without overlap&lt;/h2&gt;
&lt;p&gt;Monolithic architecture (large restaurant): Because the kitchen operates as a single unit, adding a new dish (or a feature/technology in software) requires retraining all chefs, redesigning workflows, and potentially remodeling the kitchen. Miscommunication between staff can lead to delays or mistakes that impact the entire customer experience.&lt;/p&gt;
&lt;p&gt;Service architecture (food trucks): While each truck operates independently, there’s still a festival coordinator (like a shared infrastructure team) ensuring there’s enough power, parking, and crowd management. However, each truck controls its own menu, and most changes do not require coordination with others.&lt;/p&gt;
&lt;h2 id=&quot;growth-and-flexibility&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#growth-and-flexibility&quot; aria-label=&quot;growth and flexibility permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Growth and flexibility&lt;/h2&gt;
&lt;p&gt;Monolithic architecture (large restaurant): Transitioning to a new cuisine or scaling up to meet growing restaurant popularity is difficult. Remodeling the kitchen, retraining staff, and introducing new workflows is time-consuming and costly. The rigid structure makes it hard to adapt to changing customer preferences or introduce new offerings.&lt;/p&gt;
&lt;p&gt;Service architecture (food trucks): Trucks can be easily swapped, removed, or added based on customer demand. If a new food trend emerges, a new truck can join without affecting the existing ones. It’s much easier to bring in a new truck than to remodel an entire restaurant kitchen.&lt;/p&gt;
&lt;h2 id=&quot;resource-usage-efficiency&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#resource-usage-efficiency&quot; aria-label=&quot;resource usage efficiency permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Resource usage efficiency&lt;/h2&gt;
&lt;p&gt;Monolithic architecture (large restaurant): The kitchen must handle all kinds of orders simultaneously, whether it’s burgers, vegan dishes, or desserts. If a large catering order comes in for desserts (akin to batch processing a large dataset in software), the kitchen must dedicate significant resources to it, slowing down service for regular diners. Customers of the restaurant have to wait until the catering order is completed.&lt;/p&gt;
&lt;p&gt;Service architecture (food trucks): If there’s a long line at the burger truck, another identical truck can be brought in to handle the load. Other trucks (e.g., vegan or dessert) remain unaffected. If there’s a large catering order for desserts, a specialized dessert truck can park in a separate area, focus on fulfilling the batch, and leave when done - without disrupting the rest of the festival.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;A well-known proverb says, &lt;em&gt;&quot;Too many cooks spoil the broth.”&lt;/em&gt; This perfectly describes the challenges of large teams working within a monolithic architecture.&lt;/p&gt;
&lt;p&gt;Summarizing the benefits of moving to a services architecture:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Team autonomy: By switching to a services architecture (or the food truck approach), teams gain greater autonomy, much like how food truck staff can focus on their own recipes without interference.&lt;/li&gt;
&lt;li&gt;Better and more granular scalability: Just like adding more food trucks can handle high demand without disrupting existing operations, services architecture allows for seamless system scaling.&lt;/li&gt;
&lt;li&gt;Fault tolerance: If one service (or food truck) fails, others continue running, reducing overall downtime.&lt;/li&gt;
&lt;li&gt;Limited coordination overlap: Each team (or truck) manages its operations independently, requiring minimal coordination.&lt;/li&gt;
&lt;li&gt;Flexibility: Services architecture enables quick feature introduction or strategy shifts, just like a food truck can easily switch cuisines.&lt;/li&gt;
&lt;li&gt;Efficient resource usage: Services architecture optimizes resource allocation, much like adding individual food trucks based on demand rather than overloading a single restaurant kitchen.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;final-thought&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#final-thought&quot; aria-label=&quot;final thought permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Final Thought&lt;/h2&gt;
&lt;p&gt;This analogy was meant to provide a straightforward explanation without getting lost in countless corner cases (of which we have more than a dozen for each section). While we acknowledge that both monolithic and service architectures have their strengths and weaknesses, two key factors particularly favored the services approach in our case: multiple teams developing the project independently and the unknown scale of the project.&lt;/p&gt;
&lt;p&gt;A restaurant is often associated with quality, luxury, and a certain standard - just like a monolith, where the rules are the same for all teams, providing consistency. However, this centralized approach requires strong management and coordination, replacing autonomy with strict rules, monitoring, and validation. For example, a single tomato supplier affects all chefs - if there’s a problem with the delivery, the entire restaurant is impacted. Managing such dependencies and coordinating changes can become a significant operational cost.&lt;/p&gt;
&lt;p&gt;On the other hand, a food festival thrives on scale and rapid product development by multiple independent teams, much like a services architecture. It allows for innovation, faster adaptation to trends, and greater resilience. However, it also requires more monitoring to ensure quality and remove underperforming food trucks.&lt;/p&gt;
&lt;p&gt;A restaurant may not work at a festival, but in a controlled environment with a predictable number of customers, it offers distinct benefits. The real question is - can we still predict customer demand in an era driven by social media and constant change?&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Hands-On with the Transactional Outbox Pattern using Python, RabbitMQ, and Elasticsearch]]></title><description><![CDATA[Introduction The Transactional Outbox Pattern is widely discussed in theory - resources like microservices.io provide excellent overviews of…]]></description><link>https://handsonarchitects.com/blog/2025/transactional-outbox-pattern-with-rabbitmq-and-elasticsearch/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2025/transactional-outbox-pattern-with-rabbitmq-and-elasticsearch/</guid><pubDate>Fri, 28 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/1dded99950cb58cffac243cb9bc9ceef/2bef9/outbox-pattern.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAADuklEQVR42nWUe0xbVRzH+cs/TJTEP/xvifEP3eIWZ6bRZEY3mFa6omzOyGY7kZVpeRRJRmXQgpulrCWbrIFLH7bltkBhSOnoNmFbeUhXXvIolVoiDFqhpcRKKb2tpfee420LhW7xd09yT3Lu53x/9/dKgf9vBE5AQD5JBmIW36fAp87i751FkGszgEUiEfJMp1Ha7fbEZ0/CEEQAvgnhRji0bGpBMK8thP0+8oAfDNggDN+X1tRd5TqWV+L8LkwQ0ct6zAZFlxhCR9A3ZhBke+3XV5ckkybh2LB+8bF5YWFi1jrT3n7L5/MlKccc+XfUpjcMqonwVHANXfxTe1cvnO2/0NtRuuL2kRHY2toymYZsttnoXyRgAHBS2+GcGh7vAgGzZVQ9M92/5PCYh+5qZRe0KqFzYbqjXelyORsl0gYECQQCCZiMaRBCr99vMZq7JRoRu4IzOW0b/23iXq8xp4AlEx7tbGG550RLM4LFeePI6K8YtrkNx2T9GDb/JbeKXvWDsFnd1Il2tKHGh7e7DU2I9Gp3B5dRUiQSc2BED4AVwq3dVMXgdbfX+vopOk9U3f/IMGC+U3iJfbGYJbgh6Bsy3B8b5MhUZ3lXIsFJAnfg0XQQ2zARhf/2/GN5+X1K6itHUt868RKVRqVTzzAz089SX0jLTBehxxtuM/g1ODYNoAMA/y4MABm6tVXv6KtpGfsOHnn+0DsHsrKOZtMoDArlPHUf7fO0SnEGt5b5XSEemoDEPAAbe+Go265V4xsns6QKuULdnC2UF9UivDpJ4Y9K+rVGabOazfv+a05OZEMHcRsA2F6Y3AXc3on9H5y8VMFVadsvKrqqpMgjU7/qlwGGWC0Qiz/L//aby7m4vxMSTgBCscrdVd78a816mJol/UmuamnNQdouNyhuSBVlspYcsVqmQYsqr+QWn/Eta8lCAkRwLxz1wePz7E/PyMvPb1Q2MWU/l4vrBvp6FXeMX1xXlPP5tK9yWVwWHlnfEdtOVbQu5yxDpWWs1z46pWlF5Qrl+frWCkQpqpeUIii9Vn6z/mb5tepjn2R0a2sDG+5Ek6bEm2v4gTz9zRdTnnnuNJON6nqYTT01rW32xwu68T9y5To+2vYuPS819dnKvLfnLH1gu2djyvFsuWdvdYpPsxmH3jt+ILO4kMbhUUq4aUVlx1glH577NPvjw8qac4u2QRwnEr30RD97IG4mXJr1qeoVc+XcwxLbvQKrocA1Uh1y6onw2s6ceAqOTw086S6yeMIwaRAlDaX/AJu3o86j+We7AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Transactional Outbox Pattern hero image&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/1dded99950cb58cffac243cb9bc9ceef/2bef9/outbox-pattern.png&quot;
        srcset=&quot;/static/1dded99950cb58cffac243cb9bc9ceef/ff46a/outbox-pattern.png 325w,
/static/1dded99950cb58cffac243cb9bc9ceef/a6d36/outbox-pattern.png 650w,
/static/1dded99950cb58cffac243cb9bc9ceef/2bef9/outbox-pattern.png 1024w&quot;
        sizes=&quot;(max-width: 1024px) 100vw, 1024px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;introduction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#introduction&quot; aria-label=&quot;introduction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Introduction&lt;/h2&gt;
&lt;p&gt;The Transactional Outbox Pattern is widely discussed in theory - resources like &lt;a href=&quot;https://microservices.io/patterns/data/transactional-outbox.html&quot;&gt;microservices.io&lt;/a&gt; provide excellent overviews of the concept. However, finding practical, code-based examples can be challenging. This pattern tackles a common challenge in distributed transactions: ensuring atomicity between database operations and message publication. In distributed systems, a failure occurring between the successful storage of data and the dispatch of an event can lead to inconsistencies. In this blog post, we present a reference implementation of the pattern using Python, RabbitMQ, and Elasticsearch. Our goal is to provide a hands-on guide that bridges the gap between theory and real-world application, particularly for those building global-scale web applications and customer experience platforms.&lt;/p&gt;
&lt;h2 id=&quot;example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#example&quot; aria-label=&quot;example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Example&lt;/h2&gt;
&lt;p&gt;Our GitHub repository, &lt;a href=&quot;https://github.com/malaskowski/outbox-pattern-example&quot;&gt;transactional-outbox-pattern-example&lt;/a&gt;, contains a complete, runnable project that demonstrates the Transactional Outbox Pattern. In this example, we simulate a simple items catalog system. When a new item is added or updated, the change needs to be propagated to other parts of the system.&lt;/p&gt;
&lt;p&gt;The example uses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python for the application logic,&lt;/li&gt;
&lt;li&gt;RabbitMQ as the messaging broker,&lt;/li&gt;
&lt;li&gt;Elasticsearch as the document database (instead of a traditional relational database).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The project is structured into three main components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Producer: Saves data into Elasticsearch, including an outbox property.&lt;/li&gt;
&lt;li&gt;Relay: Since Elasticsearch lacks built-in change data capture, a dedicated Relay component polls the database for unsent outbox entries and publishes them to RabbitMQ.&lt;/li&gt;
&lt;li&gt;Consumer: Listens for messages from RabbitMQ and processes the updates accordingly.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/8fe823b13cc7f833ed20793071b8b942/aea0a/reference-diagram.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.69230769230769%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAABhUlEQVR42k1Qy0rDQBTNZxQRcaUiii78F/9CUPATqiiK1VJcuXBVF64UHxtBRBdubOmTpkkamubRSdLpNCYznSSNtw1Cz2bmHs65jyP8skhyxrIzDnicJJM4jifJFJTHQLZt5rEIypQE8GjSGYxFm+EgFE4/8MqFuXJh3P54MSVKR/31AxDdfDvrV+Zqzjx+x1BCy7TrcwNv5q21nLn3aAuHr4NMVslkO+efJOG+afVNC42QfnBXWzrTF47V/Sd3Zp6AN6ajk4fq8llv8UjaLfaFy6/hRt7aKqBi2UsnuHiYhMF9zd8uWJt5M/dF0oXxkNhG900c7VwDbx2+OALcgAiTeogyzii1HQdjzBi1+khDxMKBppvAR1Hsuq6qqjyMsB/ZHodQhGkGnMuSOHBdRVFEUZQkSZblcrlMCAnDsFatNhoNsLXbbU3TINH/7JKpGWpKKQwEQ7PZbLVa9XodIZQqoAXwlUqlVCp1u91kDkL6zOKY6nRdNwwDPvMiWM2bwff9efMf0YOk3HIdm4cAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Reference implementation diagram&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/8fe823b13cc7f833ed20793071b8b942/3c492/reference-diagram.png&quot;
        srcset=&quot;/static/8fe823b13cc7f833ed20793071b8b942/ff46a/reference-diagram.png 325w,
/static/8fe823b13cc7f833ed20793071b8b942/a6d36/reference-diagram.png 650w,
/static/8fe823b13cc7f833ed20793071b8b942/3c492/reference-diagram.png 1300w,
/static/8fe823b13cc7f833ed20793071b8b942/d544a/reference-diagram.png 1950w,
/static/8fe823b13cc7f833ed20793071b8b942/aea0a/reference-diagram.png 2018w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/b5920f1b78d49a9cebff6595729831dd/aea0a/reference-diagram-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 39.69230769230769%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAPoAAAD6AG1e1JrAAACFElEQVR42mMwqz/Bp9p/X1Ot576G8cwzXAxA8P//fyYGKLDofcSp1nNDQ6X1lpZxxxl+BjRgPPM/q0bPXTXljtvaGm1PhBlkmq73SrTc/S/Zeve/dMO1QqBhjIcuXFObc+Q6L0iDeuf1Uommm/8lmu/8l2m+MQFsSj3QwtBVzCCmdufVcPGmG78lmm//l266sY5BuvPxPPkJr/8r9L/6L93+uAmkaMfBU7LTlm5R+v9pqYjTrEcLpDqf/pfrffEfSC+EG8jwn/H//3qmwPkP2qQ6Hv1X6nv2X7bz4UEGoK0tQBv+ijXc+CXTfC0brAFo+6UP/wVBTMmmaylijTd+SDRe/yvVfL0N5tVLDz8I/v9/R0yz9VKARNONz+KNN/5IN99YyqBVf4XNrGO/TPm8A7pA77JaGRsrm5qaRhkZ6YdZWBiqfPr/XzRhwi5V+/btCnvP3pNfNWUKj5yurmB9fb3Shg0btOpXrWJT6zkjotFyUNK4HhIHDNu2bWM/f/KwNtBAdmdn51pdXd2r2tral4HsRqCY1qpVc0XT0ny4/v/7p5mYmJippaW1U19f/6CFhUU9AzYAtI0JiAWAmjkDAgImGRoaXgFquOTp6TkRKCYEVAKKAGaQfEJCQqW5ufllS0vLGyYmJn1QIxixmQtOKkZGRk5Awybo6en1AzW4IGkAa7K3t5fQ0dFxBcp72NjYaKEbCACAn+QqftB9NgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Reference implementation diagram&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/b5920f1b78d49a9cebff6595729831dd/3c492/reference-diagram-dark.png&quot;
        srcset=&quot;/static/b5920f1b78d49a9cebff6595729831dd/ff46a/reference-diagram-dark.png 325w,
/static/b5920f1b78d49a9cebff6595729831dd/a6d36/reference-diagram-dark.png 650w,
/static/b5920f1b78d49a9cebff6595729831dd/3c492/reference-diagram-dark.png 1300w,
/static/b5920f1b78d49a9cebff6595729831dd/d544a/reference-diagram-dark.png 1950w,
/static/b5920f1b78d49a9cebff6595729831dd/aea0a/reference-diagram-dark.png 2018w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The diagram illustrates how these components interact to ensure that every data change is eventually communicated through the message broker.&lt;/p&gt;
&lt;h2 id=&quot;how-it-works&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-it-works&quot; aria-label=&quot;how it works permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How It Works&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Producer Persists Data with Outbox Metadata:&lt;/strong&gt; The process begins when the Producer receives a request to create or update data. In a single atomic transaction, it writes the new data (document) into Elasticsearch, setting an &lt;code class=&quot;language-text&quot;&gt;outbox_sent&lt;/code&gt; flag to &lt;code class=&quot;language-text&quot;&gt;False&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Relay Polls for Unsent Outbox Entries:&lt;/strong&gt; The Relay component continuously polls Elasticsearch for documents that remain unprocessed. Since Elasticsearch does not offer change data capture, this polling mechanism is essential.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Relay Publishes Messages to RabbitMQ:&lt;/strong&gt; When the Relay finds an unprocessed document, it publishes the corresponding event to RabbitMQ.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Relay Updates the Outbox Record:&lt;/strong&gt; After successfully sending the message (using &lt;a href=&quot;https://www.rabbitmq.com/docs/confirms#publisher-confirms&quot;&gt;publisher confirms&lt;/a&gt;), the Relay updates the outbox record to mark it as &quot;sent&quot;. If an error occurs, the document remains in a pending state until the issue is resolved. If the database update fails, the outbox record is not marked as &quot;sent&quot;, and the message is retried in the next polling cycle. This ensures at-least-once delivery semantics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consumer Processes the Message:&lt;/strong&gt; The Consumer, subscribed to RabbitMQ, receives the published message. It processes the event accordingly - this could involve updating its local state, triggering business logic, or further propagating the change.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;ensuring-consistency-in-failure-scenarios&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ensuring-consistency-in-failure-scenarios&quot; aria-label=&quot;ensuring consistency in failure scenarios permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Ensuring Consistency in Failure Scenarios&lt;/h2&gt;
&lt;p&gt;To make this as practical as possible, our repository allows you to simulate various real-world failure scenarios that the Transactional Outbox Pattern is designed to handle.&lt;/p&gt;
&lt;p&gt;One of the challenges in distributed systems is ensuring that once data is stored in the database, it is reliably sent to the message broker. The pattern we present today addresses this challenge effectively. In our implementation, the Relay service plays a crucial role. When data is published by the Producer, it is written to Elasticsearch along with an outbox property. If the message broker becomes unavailable, the Relay detects the issue and does not mark the outbox item as &quot;sent&quot;. Once the broker is restored, the Relay picks up all pending items and publishes them to the Consumer via RabbitMQ. This mechanism ensures that no data is lost and that all updates eventually propagate throughout the system.&lt;/p&gt;
&lt;p&gt;You can explore additional failure scenarios, such as Producer Failure, Relay Failure, Consumer Failure, or even a Data Store Outage, by following the instructions in the repository’s README.&lt;/p&gt;
&lt;h2 id=&quot;caveats-and-considerations&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#caveats-and-considerations&quot; aria-label=&quot;caveats and considerations permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Caveats and Considerations&lt;/h2&gt;
&lt;p&gt;While the Transactional Outbox Pattern is a powerful approach for maintaining consistency in distributed systems, it has its trade-offs. Here are a few considerations specific to this implementation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Message Ordering: The pattern does not guarantee message ordering. If order is critical, additional mechanisms are required or you may want to consider alternatives to the Transactional Outbox - “Listen to Yourself” pattern.&lt;/li&gt;
&lt;li&gt;Message Duplication: The pattern can lead to message duplication in some failure scenarios (due to at-least-once delivery). The Consumer should be idempotent to handle such cases.&lt;/li&gt;
&lt;li&gt;Resource-Intensive Polling: The Relay component continuously polls Elasticsearch for unsent outbox entries. This can be resource-intensive and may not be suitable for high-throughput systems.&lt;/li&gt;
&lt;li&gt;Delayed Propagation: The pattern introduces a delay between when data is written to the database and when it is processed by the Consumer. This delay can be significant depending on the polling interval, processing time, and the time it takes Elasticsearch to index the data. These factors combined may make the pattern unsuitable for real-time systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;In this post, we explored a hands-on example of the Transactional Outbox Pattern implemented with Python, RabbitMQ, and Elasticsearch. We explained the data flow between the Producer, Relay, and Consumer, and demonstrated the pattern&apos;s behavior in a broker failure scenario. We also discussed trade-offs and considerations to keep in mind when implementing this pattern in your own systems. We encourage you to clone the repository, experiment with the code, and consider how this pattern can enhance the resilience of your own distributed systems.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Practical DevOps approach at Poznań Java User Group]]></title><description><![CDATA[After a short break from blogging, we are back with fresh insights from our recent presentation at the Poznań Java User Group on May 22nd…]]></description><link>https://handsonarchitects.com/blog/2024/practical-devops-approach-at-poznan-java-user-group/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2024/practical-devops-approach-at-poznan-java-user-group/</guid><pubDate>Thu, 30 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/17751addb013263060e9d6fbda064973/c83bb/handsonarchitects-at-poznan-jug.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.07692307692308%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMEAv/EABYBAQEBAAAAAAAAAAAAAAAAAAACA//aAAwDAQACEAMQAAABrWrBUTmdf//EABsQAQADAAMBAAAAAAAAAAAAAAEAAgMREhMi/9oACAEBAAEFAu9pbSxoaLKH16cQ2E//xAAWEQEBAQAAAAAAAAAAAAAAAAAAEQH/2gAIAQMBAT8BuK//xAAWEQEBAQAAAAAAAAAAAAAAAAAAARL/2gAIAQIBAT8BsYf/xAAbEAACAgMBAAAAAAAAAAAAAAAAARExEBIhMv/aAAgBAQAGPwLzxkIo1lkU08f/xAAaEAACAwEBAAAAAAAAAAAAAAABEQAhQWEx/9oACAEBAAE/IRugGnFCrOxkSZukIL1umESUUTyPFz//2gAMAwEAAgADAAAAEBjv/8QAFhEBAQEAAAAAAAAAAAAAAAAAAQBh/9oACAEDAQE/EBGzf//EABYRAQEBAAAAAAAAAAAAAAAAAAEAEf/aAAgBAgEBPxBJK6l//8QAHRABAQACAQUAAAAAAAAAAAAAAREAIUExYXGhsf/aAAgBAQABPxC0RtoK+/OGUJhPTT5ie7FQJxkkQ2rQQ4ODCh0EDQM1Y4ywL3z/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;HoA at Poznan Java User Group&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/17751addb013263060e9d6fbda064973/01ab0/handsonarchitects-at-poznan-jug.jpg&quot;
        srcset=&quot;/static/17751addb013263060e9d6fbda064973/c989d/handsonarchitects-at-poznan-jug.jpg 325w,
/static/17751addb013263060e9d6fbda064973/6aca1/handsonarchitects-at-poznan-jug.jpg 650w,
/static/17751addb013263060e9d6fbda064973/01ab0/handsonarchitects-at-poznan-jug.jpg 1300w,
/static/17751addb013263060e9d6fbda064973/6951b/handsonarchitects-at-poznan-jug.jpg 1950w,
/static/17751addb013263060e9d6fbda064973/46378/handsonarchitects-at-poznan-jug.jpg 2600w,
/static/17751addb013263060e9d6fbda064973/c83bb/handsonarchitects-at-poznan-jug.jpg 4608w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;After a short break from blogging, we are back with fresh insights from our recent presentation at the &lt;a href=&quot;https://www.meetup.com/pl-PL/poznan-java-user-group/events/300745537/&quot;&gt;Poznań Java User Group on May 22nd&lt;/a&gt;. The topic? A practical approach to software lifecycle optimization for Java applications within complex systems. The session was well-received, underscoring the growing interest in integrating DevOps practices within the Java community.&lt;/p&gt;
&lt;p&gt;Despite a late start (just after 8 p.m.) following a full working day, we were positively surprised by the audience&apos;s enthusiasm and involvement. The session concluded with an almost 20-minute Q&amp;#x26;A, reflecting the high level of engagement. The presentation covered three critical areas: working effectively with complex systems in development environments, continuous integration, and a comprehensive approach to testing, including unit, integration, and end-to-end testing.&lt;/p&gt;
&lt;p&gt;A key takeaway was the positive reception of DevOps principles by the Java developers in Poznań. The audience&apos;s insightful questions and active participation demonstrated a keen interest in adopting and refining these practices to enhance productivity and efficiency. During the presentation, we showcased several DevOps tools such as LensIDE, Kind, and GitHub Actions, which sparked significant interest and curiosity among attendees. Many questions revolved around the practical implementation of these tools and their integration into the software development lifecycle.&lt;/p&gt;
&lt;p&gt;Although the presentation was not focused on the Java language itself, the feedback and interactions from this event have been incredibly encouraging. It’s clear that there is a vibrant and eager community ready to explore these topics. We are excited about the possibility of bringing this discussion to other forums and user groups, fostering a broader conversation around optimizing the software lifecycle for Java and other communities.&lt;/p&gt;
&lt;p&gt;Stay tuned for more insights and updates as we continue to explore and share best practices in software development and DevOps. Thank you to the Poznań Java User Group for hosting such an inspiring event and to all the participants for their enthusiastic involvement. We look forward to more enlightening sessions in the future!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[HandsOnArchitects celebrates its first anniversary]]></title><description><![CDATA[As HandsOnArchitects marks its first anniversary, we're excited to revisit our top-performing blog posts that have resonated with our…]]></description><link>https://handsonarchitects.com/blog/2024/celebrating-first-anniversary/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2024/celebrating-first-anniversary/</guid><pubDate>Tue, 12 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/5fa8839a6b1e9e4f91cca05f2e03bb67/e5715/first-anniversary.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEr0lEQVR42i1UeUyTZxj/MpNlcYduUTeZHC1tv34tOHVyD1Yt0tLjO1oF5jEUdYABgiLigQhTKyJoqVRL+11tqZQjHA3UgDjMQDKUgYEtQXQop1s0ZpJNowLf3polzx9v3jzP77l+zw+iYZQSoySspWGMhFESwWgYB29KjNlEWgaYGCMlGCkG/xgj1FAwTiI6UoSSQg1klxA0n7DxCRomaAQFfpQYZ/4P8+HSwCQ4g2DXghMtsjR6rd4lUANogAKZg7DquDhKHktKFUyA2g5KAMnFGEgIHhQwMVoTrKwM0RfEZabqTx1QHC8K32+DUUaohUZ6l7+cX7KwAN0bWHZx++bLPMzOV1LBIJgA2UiJvpanSEHPLbVMCmseB9hGA6mJZc5nvKK+qpAUiOPeW3wL3RuX2oeGGqfnzZ6RS7JMJlzOiOW0AGWlOM1XG7Spnd8pH8hCJ+LWTcX430+UOnZuN63XQc/+fp/szqsZHm98xDmGX7Y/5ax3//UMlrpOp1j4W1lYbYF1N0Mkb6M+ehi9tl+BP/xm06vI5VPrP3WIZJCnX3thgGud4pjeid3E5rN2r3uMa/DU1rEVV6U6B+hNglWLCeOXyeaosFuylZY1IUae5gpYjQiFSrtH6OFF5xhXe/f+7QrM3ny96jeuzVrYQpadOWixClR20LkAb9R/favog8E8aLAEYpURNF/HIChUNrBQNcRd6Hm+v9Jz5kh6NtVTNTTffqOT7hm9OstdyKpkAhQOWJlryMC7SLnDqPZYT+1NsQclkhIC+rH3dfkgpzD2xO8/mYnKBJlMCeN59eRB3+Cv5SOvz5a3UsFKZpV6X1Y7doPT17/WeLn8LeWsn4KU4NCxW/Nn7nBx1jHZeW9HsaqqOKOQammqzDEeik83ucrSysjViXXbIxq84Wx/Um2f1t2nb6v/ipHH2vg4lNH+ItvzZ6KpX6A7lpmcYDmRfGiPeilP9TGMf/JF+FmBxiHY5twW01nC/6nAv+u4f2u+X4eBz8ZvsvExaF+exTXOGVzdssjw6J2FG7cd+TZsoyrn0sZd55fCupSo72kJWh2gu7IqpXxVUtnKrYYVRNXKZIpPUGBgV3tnf57jimtuCPzXFJqcppYeiQg5RbZkmxrO5e44mp9aVGIHA6+WaIxI4hWJxoKo7KHvmAu43TG96H3MWa7flet2GtsG3cN/4Tv2srcnvJMLFXVe8ubv7Pirwri9Zt4WSkqUwSpAW9+pAOaDPdf/wbU8WnSP/dMwudA+wzkHZupGnjZNL7pnOffMousZd7R1vFJKOAPiKwJlzsB4Bzg15B3zYRRKvz6X3fm80PrLcbI/v+fFiTvc4X4uhxo4aPDmFjcXpJsL5DmnN6TGHO3YcLKrIDbLHJpEi7WkDwKFLsYeKI35wRqYYF0ty91nSm96kmEfLY1Kc/rJGL7S8Xl09OGmDxvnVrjnPmt+s6TlzS79+WtBCSQCBEMLXQtWuXigE4xFcIqXcCk0yYzo7AINhYB54uCYz4XtLonNPB2xxxCZVhKTUbluByiY9IkBCjmkOhYhgGgwCJALPStCWd9ICOqdByPCHEKsRqh1iHzmBPICoz49EuM2GP8PruQxFNQzVJsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;First HoA anniversary&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/5fa8839a6b1e9e4f91cca05f2e03bb67/e5715/first-anniversary.png&quot;
        srcset=&quot;/static/5fa8839a6b1e9e4f91cca05f2e03bb67/ff46a/first-anniversary.png 325w,
/static/5fa8839a6b1e9e4f91cca05f2e03bb67/a6d36/first-anniversary.png 650w,
/static/5fa8839a6b1e9e4f91cca05f2e03bb67/e5715/first-anniversary.png 768w&quot;
        sizes=&quot;(max-width: 768px) 100vw, 768px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As HandsOnArchitects marks its first anniversary, we&apos;re excited to revisit our top-performing blog posts that have resonated with our amazing community.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As we celebrate our first anniversary, we&apos;re excited to revisit our top-performing blog posts that have resonated with our amazing community.&lt;/p&gt;
&lt;h2 id=&quot;simple-trick-to-use-dynamic-values-in-helm&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#simple-trick-to-use-dynamic-values-in-helm&quot; aria-label=&quot;simple trick to use dynamic values in helm permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Simple Trick to Use Dynamic Values in Helm&lt;/h2&gt;
&lt;p&gt;Our guide on leveraging dynamic values in Helm charts has been a go-to resource for Kubernetes enthusiasts. This simple yet quite effective trick has helped readers streamline their Helm deployments, making it a valuable asset for those navigating the complexities of Kubernetes. Expect more insightful content on Kubernetes and Helm in the year ahead! Read more &lt;a href=&quot;https://handsonarchitects.com/blog/2023/simple-trick-to-use-dynamic-values-in-helm/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;manage-complexity-for-demonstration-environments&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#manage-complexity-for-demonstration-environments&quot; aria-label=&quot;manage complexity for demonstration environments permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Manage complexity for demonstration environments&lt;/h2&gt;
&lt;p&gt;Team dynamics are crucial in the tech world, and our post on building goal-oriented micro teams struck a chord with team leaders and developers alike. By diving into the principles of effective team structuring, we emphasize the importance of clear goals and streamlined communication. We will continue to explore the dynamics of high-performing teams in the next year. Read more &lt;a href=&quot;https://handsonarchitects.com/blog/2023/manage-complexity-for-demonstration-environments/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;how-to-manage-your-time-as-a-software-engineer---5-practical-tips-and-tools&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-manage-your-time-as-a-software-engineer---5-practical-tips-and-tools&quot; aria-label=&quot;how to manage your time as a software engineer   5 practical tips and tools permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to Manage Your Time as a Software Engineer - 5 Practical Tips and Tools&lt;/h2&gt;
&lt;p&gt;Time management is an ongoing challenge in the fast-paced realm of software development. Although it is one of the most recent posts, it has quickly become one of most popular among our readers. As we reflect on our first year, these time management strategies continue to be a beacon for developers seeking balance. There is a chance you will hear more about this topic from us, maybe in the form of presentations or workshops. Read more &lt;a href=&quot;https://handsonarchitects.com/blog/2024/how-to-manage-your-time-as-software-engineer-5-practial-tips-and-tools&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;just-in-time-knowledge-sharing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#just-in-time-knowledge-sharing&quot; aria-label=&quot;just in time knowledge sharing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Just-in-Time Knowledge Sharing&lt;/h2&gt;
&lt;p&gt;In this blog, we touch on the concept of just-in-time knowledge sharing that we started to introduce in our teams in 2021. This post underscores the importance of timely knowledge transfer within development teams. This position resonated with our community&apos;s thirst for continuous learning. As we celebrate this milestone, we extend our gratitude to our community for making the first year of HandsOnArchitects truly exceptional.
Read more &lt;a href=&quot;https://handsonarchitects.com/blog/2023/just-in-time-knowledge-sharing/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We look forward to another year of insightful content and meaningful connections. Thank you for being part of our journey!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Maciej &amp;#x26; Tomek - hands-on software architects&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How incorporating an integration layer in Adobe Experience Manager (AEM) project saved our clients money]]></title><description><![CDATA[Adobe Experience Manager (AEM) is more than a content management solution for building websites. It is a powerful tool that allows you to…]]></description><link>https://handsonarchitects.com/blog/2024/adobe-experience-manager-integration-layer/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2024/adobe-experience-manager-integration-layer/</guid><pubDate>Tue, 27 Feb 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/3c7f24c93f59a5a797b2c79355cabe63/e5715/composable-aem-architecture.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAACzUlEQVR42o2UTUwTQRTHh1Lh3GDUyAFDvKjEr8SLiYGL8cjJGG5ENFGDJoQCiqUCldgqTUMi+FGMJMhBa7ChUcKhiR+lwX4FS1qhEUu70M+l25alX7sz49BVaBQq7/CyeW9+8/7vzewAhBD+Y+vr6zabLZPJ4N0ZEGCe54nX6XQajcZgMAiRwn2LwRBC4o1GY1dXl9ls3oz8HxbWOZ1OrVZLyg4PD7vd7kAgEIvFSLxI/S3YYrH4fD7y4fV6TSYTTdPpdHpXsgXPcRxhdhJM4n+pAAIpRHPZXDQSFRKki4mJicLsv1sAlM/OzbuMpo+fvpomjVMmi3naNvNi5IW6v1+AU6nUfN5cLlcymdzs9Lfs9091qksdI9KBkbaB582qBxfbrJMmEs9ms8RHIhG1Wj00NKRUKj0ez4bGXC4Pww3YfHN0THJ9/FSnoa7n7dGOMckNx8CHJJcKBYIsy5I6drudHIHD4bBareFwWNAPiOMRZvVyRn6CftwQ0V6LaeoZ+fHE55f+EA25nCB7cHBQr9eTi6BQKIRZ5GHEkwnw6VXEUtnkylpsmWeDOBO0znyZm3MJPZNToCgqGo2Shv1+P8MwQrMA8RyKLUL6B4wtZSKeNcqBE0vcqpf1zfLx5c0JF459a9rQ8gx2AigT4Xsg3AKMDSKqGeDuEiQDsK8CrTg2ViGICmzrnOGbRtQKcI84KQXNZytqj1U1njmwfAvgXjFsAcg+mi/NbX/D4LsmKAVYsWfuasmFk9XnDkvqaqqnGsS4T8y1AjT7qig83oTbSeXyXGfJ/fOS+tOH2mv3JtpKcW85ug3Qt+Kw7jJuAVhWyt8V0R3llitlAWkZkokyd8QLjSBkfFIMRs7XWLkPP6yEqoNpVVW8/wirqsaPKqPy/QvdNd+nDclUdqcfM38910KYWUJxH477IONDjB8nqBz9k1p0B0NhmH9ktq38CxLz/zTdgXsRAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Composable AEM architecture&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/3c7f24c93f59a5a797b2c79355cabe63/e5715/composable-aem-architecture.png&quot;
        srcset=&quot;/static/3c7f24c93f59a5a797b2c79355cabe63/ff46a/composable-aem-architecture.png 325w,
/static/3c7f24c93f59a5a797b2c79355cabe63/a6d36/composable-aem-architecture.png 650w,
/static/3c7f24c93f59a5a797b2c79355cabe63/e5715/composable-aem-architecture.png 768w&quot;
        sizes=&quot;(max-width: 768px) 100vw, 768px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://business.adobe.com/products/experience-manager/adobe-experience-manager.html&quot;&gt;Adobe Experience Manager&lt;/a&gt; (AEM) is more than a content management solution for building websites. It is a powerful tool that allows you to create, manage, and deliver digital experiences across different channels. However, when it comes to integrating AEM with other systems, it can be challenging to ensure seamless communication between different platforms. We learned that during work on several global brand projects in earlier stages of our careers.&lt;/p&gt;
&lt;p&gt;The recurring challenge of integrating AEM with other systems, such as product information management (PIM), while also ensuring that experiences are delivered in a performant and scalable way to the end-users pushed us to create a dedicated integration layer that helped us to overcome these challenges. This layer was designed to ease delivering complex platforms consisting of AEM (or other CMS) and systems that were delivering content, data, or services. What is more, the concept of the integration layer is still valid today, and we believe that it can be beneficial for many AEM projects. This is why we decided to share our thoughts on this topic with you.&lt;/p&gt;
&lt;h2 id=&quot;when-you-dont-know-what-it-is-about---its-all-about-licensing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#when-you-dont-know-what-it-is-about---its-all-about-licensing&quot; aria-label=&quot;when you dont know what it is about   its all about licensing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;When you don&apos;t know what it is about - it&apos;s all about licensing&lt;/h2&gt;
&lt;p&gt;For those who work with Digital Experiences Platforms, it should be no surprise that a major concern was related to the financial aspect. AEM licenses (on-premises) come at a significant cost, and reducing the number of AEM Publish instances yields substantial savings. Decreasing the number of Publish instances from &lt;code class=&quot;language-text&quot;&gt;10+&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt; was a transformative step for both international and regional brands.&lt;/p&gt;
&lt;p&gt;This transition was made possible by redefining the responsibilities of CMS-centric systems and introducing a dedicated integration layer. Instead of housing all business logic and integrations within a monolithic system, we shifted AEM&apos;s role to focus solely on content management (which is its core strength) and the generation of static pages, fragments, or templates. The heavy lifting of data processing, business logic, and integrations was shifted to a place that was designed to handle these tasks efficiently. This approach allowed us to reduce the number of AEM Publish instances, which resulted in significant cost savings.&lt;/p&gt;
&lt;h2 id=&quot;performance-and-scalability&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#performance-and-scalability&quot; aria-label=&quot;performance and scalability permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Performance and scalability&lt;/h2&gt;
&lt;p&gt;Another challenge we faced was related to performance and scalability. AEM is a powerful tool, but it is not designed to handle high traffic or complex integrations. We learned it the hard way not once or twice. This was especially true during peak loads, such as marketing campaigns. Scaling AEM instances to handle these loads was not only costly but also inefficient.&lt;/p&gt;
&lt;p&gt;The thin integration layer, a lightweight application written in a reactive paradigm, required significantly less CPU and RAM and was a much better fit for these tasks. Transferring CPU-intensive tasks, such as rendering pages with dynamic content like product details, ratings, prices, cart items, or personalized offers, to the integration layer allowed us to deliver performant and scalable experiences to the end-users. Not to mention reducing the infrastructure costs compared to traditional, CMS-centric architecture.&lt;/p&gt;
&lt;h2 id=&quot;feasibility-and-maintainability&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#feasibility-and-maintainability&quot; aria-label=&quot;feasibility and maintainability permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Feasibility and maintainability&lt;/h2&gt;
&lt;p&gt;The third money-saving factor for introducing an integration layer was related to the feasibility and maintainability of the solution. Developing and maintaining AEM projects is expensive. By moving part of the platform outside of AEM, we were able to execute projects with fewer AEM developers, who tend to be quite expensive.&lt;/p&gt;
&lt;p&gt;After delivering the first projects it became clear that a great part of the integration patterns, such as circuit breakers, API caching techniques, API fallback strategies, sequenced API invocations, and more, can be abstracted and exposed via &quot;no-code&quot; configuration. This allowed our projects to address integration challenges, not only during happy-case scenarios but also in the event of failures, such as temporary unavailability of APIs. This approach made it easier to find developers with the right skill set and made it easier to maintain the solution for many years. The integration layer was also designed to be modular, which made it easier to add new features or integrations. This was crucial for brands that were constantly evolving and adding new systems to their digital experience platforms.&lt;/p&gt;
&lt;h2 id=&quot;composability-and-flexibility&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#composability-and-flexibility&quot; aria-label=&quot;composability and flexibility permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Composability and flexibility&lt;/h2&gt;
&lt;p&gt;There is one more cost-related reason to introduce an integration layer in AEM projects. While integrating data inside AEM is possible, it leads to a tightly coupled solution where replacing or adding new systems is difficult and costly.&lt;/p&gt;
&lt;p&gt;The integration layer that we introduced was designed to bring composability into the digital experience platform. It enabled combining different integrations and features to create a custom solution that fits the specific needs of the brand and incorporate the tools that were already in use and people were used to. This was especially important for global brands that had different systems and requirements in different regions. The integration layer allowed us to create a custom solution for each region, while still maintaining a consistent user experience across different channels.&lt;/p&gt;
&lt;p&gt;Additionally, the flexibility that came with the integration layer allowed developers to use the best tools for the job. To name a few examples from the projects we worked on, we integrated search engines using Apache Solr, personalization engines, Commerces, PIMs, and more. This flexibility allowed us to effectively solve specific problems, rather than being confined to Adobe&apos;s offerings.&lt;/p&gt;
&lt;h2 id=&quot;reusability&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reusability&quot; aria-label=&quot;reusability permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reusability&lt;/h2&gt;
&lt;p&gt;That brings us to the last point. Gathering experience from multiple projects, we realized that the integration layer was not only beneficial for the projects we were working on but also for other projects. This is why we decided to wrap the integration layer into a framework called &lt;a href=&quot;https://knotx.github.io/&quot;&gt;Knot.x&lt;/a&gt;. Our solution was widely embraced across over 10 projects, including several platforms hosting more than 400 brand sites. We shared our insights and concepts at various industry events such as GeeCon, Devoxx, and numerous local tech gatherings. We open-sourced the framework and created a community around it.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The open-source project &lt;em&gt;Knot.x&lt;/em&gt; acts as a pivotal integration layer that molds customer experiences across diverse channels by blending content and layout from Adobe Experience Manager with data sourced from various repositories such as Product Information Management (PIM), Commerce, or Customer Relationship Management (CRM) systems. This integration layer enabled Cognifide / Wunderman Thompson Technology to develop sophisticated personalized &quot;dynamic&quot; websites for global brands, thereby reshaping the architecture from AEM-centric to modular and composable. Knot.x revolutionized the creation of product listing pages, product details pages, search result displays, and the dynamic adjustment of related content components like tagged content fragments/pages, blog listings, sitemaps, and navigations. Its impact on server-side generated sites was groundbreaking from development, infrastructure, and cost perspectives. Finally, the reusability of the integration layer allowed us to deliver projects faster and with fewer resources, which resulted in significant cost savings for our clients.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Navigating Secrets Consumption in Kubernetes Pods]]></title><description><![CDATA[Kubernetes offers several avenues for consuming secrets within pods and containers, each tailored to accommodate diverse requirements…]]></description><link>https://handsonarchitects.com/blog/2024/navigating-secrets-consumption-in-kubernetes-pods/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2024/navigating-secrets-consumption-in-kubernetes-pods/</guid><pubDate>Tue, 13 Feb 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 512px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/3d622bda4203af1b2205e97233f4d529/01e7c/secrets-in-kubernetes.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEz0lEQVR42gHEBDv7ABE1RhQ7Tg0uPBFEURhhcSFaaxRRXxNWZhVAUyRYbxhHUx1qdhI8SSBIWxtUYQ8yOw0nMg4sNhhCSxM0RAARQ1IkVmYhW2coc34xfIk/h5saUmM1l6MlX24/iJU6hZA4iZEqZG82YG1KjZwoYGc8i5Qyg5MxfYowa3kAG2NxMXmIG01bO4+dOXyTS4ufSpKectvkWa22VJafZK68X5ScXpahV3yHfcHPUpehbNXfVqi2OW56WqKuADZ+i1WksSpreit8jECCk1OUo0iJmXTByG+0wHewv3y/y3yvtnOtv2eTqpTO2WCVon/N12m1v1OMlUyFlQBIjZ83XGldtMVKnqxFipNutcJGe5FwsLiU5fB1r8V1tMV2prKBvM1Rd4+Pt8GEvMd8pK02OUxIaHVHdY4AWqGwWZehb8TRZqm2ktzoYJehRHiHXKW1cJmnndDddLjGao2bf6i4dZampdXfcKGthLTIb5+7VIqiT3mMAFebpVWOlmiuuHS4x5zU343O2nG3xStZZ3Keq4u5x3W4yoaruHubqW6OnHqitWmSp3uVo12KpGaKpEJhfQBoqrNpq7Z4v8p3tsNYiJ5igJuJv9BWhI+JuL1SZ35fjrqKpbOAp6+XvMet3utsmK1WQGN+mbKHscqazekAYpyoZKS1kNvlltrmi7zLhaO2kcnYk9jjjra/dpisdLTMiLC7obrJV3qfj7bHfKq1ipurjK/CcqW8XWhzAFqNnI/M1oO/yXi3xGuMnWeSpqbu9pDP2nWWnoaYuZW2zX+pt6DI1ZS705zF15zR3pW1xoqozGKSpE5UZgBJdYd7sbtsoKhurLppn693rL6d1eZ+usai2N6MrbmUtcKIusidsLeDnLpSZIJpmal6ka51lrV1o7N6rbgAUoCPY4+aZJSdb7G9YqSwWHuGlLrUgbrFkcDJdZqlo8nRqOXuq8fRobrNqcfVh7XPdZefcpqmg7zIP2Z9AE6GmWSVn3mvu3q9ymSuvH+9yJ3b5XW0xYauuZG9yHJ+k6DI1XCcsZSyzmx+mG+RuXmksmiXqIfE0nCvvwBKi6F4vMlXi5Zbjp9Zna5ekp1acoVWjKKKqreQuMNzkKKeytd9vcqBnK2Dl62Yz+FonK5ruM9dmK1upbMAUZqtUZeoUZ2sTYucXaW0dK64eLC7gsvYb628dI6WiqW1gbvLb6KtY4SWdp2zbZasg8nTerfCb6m3eLPAAE2XoEyRm1igqVmao1qgrEZteFqNmIfP2XfBznCms3misIK0voPL1liAj2uXsWClt4vQ11N+ik6GmXKyvgAzgowzdHxPn6tOmqZUrLpjqrd7ydSH4uxux9VYmqZhoKpoprCAx9Bcj556p8h2t9B+y9NLhZhDeZFCeI8AH2h0HVFZKWBsLnGBNoaYN3OBQX2GZLvGUKKxNXCAS4+cVZegd8jRT42bToSbWJexVaCsPX+OM2t5L3mLAAw6SBdRXR5bZyNlcCZmdC58iSVcZzFyfz2MmjVwgDyDlTmNl1m2wTJ2hBdGVy9dbUKYpEGSnTp5hDB4hQANMz4UTlsPKC8UNj0WSVQZU2AbVWAcWGMod4YlYnMxc4AkUVsygo0scoEbSFIiYGsznakgYGwaUl0XVWM30Hm+cG+9vQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Using secrets in Kubernetes&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/3d622bda4203af1b2205e97233f4d529/01e7c/secrets-in-kubernetes.png&quot;
        srcset=&quot;/static/3d622bda4203af1b2205e97233f4d529/ff46a/secrets-in-kubernetes.png 325w,
/static/3d622bda4203af1b2205e97233f4d529/01e7c/secrets-in-kubernetes.png 512w&quot;
        sizes=&quot;(max-width: 512px) 100vw, 512px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Kubernetes offers several avenues for consuming secrets within pods and containers, each tailored to accommodate diverse requirements. Understanding the distinctive characteristics of each option is vital for ensuring optimal functionality and resilience in your Kubernetes environment.&lt;/p&gt;
&lt;p&gt;One crucial aspect to ponder is the adaptability of secrets to potential changes. Whether your secrets might undergo alterations, expand with new values (keys), or even vanish unexpectedly, it&apos;s imperative to evaluate how these transformations could affect your pods. This is particularly crucial when apply such mechanisms as Helm charts, which can dynamicaaly calculate secrets&apos; hashes and update the pods with new values. Then pods should be recreated automatically to apply the new values.&lt;/p&gt;
&lt;p&gt;The following paragraphs will explore the various secret consumption options in Kubernetes. All scenarios below are tested on a Kubernetes cluster running version &lt;code class=&quot;language-text&quot;&gt;1.29.1&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Important:
This article does not cover any security aspects of the secrets management in Kubernetes. It is important to remember that secrets are not encrypted by default and are stored in etcd in base64 encoded form.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;secret-as-volume&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#secret-as-volume&quot; aria-label=&quot;secret as volume permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Secret as Volume&lt;/h2&gt;
&lt;p&gt;We begin our exploration with the secret as a volume, a method that mounts the secret data entries as files within the pod&apos;s filesystem. This approach is particularly useful when your application requires access to the secret as a file, such as when reading a certificate or key file.&lt;/p&gt;
&lt;p&gt;To illustrate the behavior of a pod with a secret mounted as a volume, consider the following scenario:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a pod with the secret mounted as a volume.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Pod
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pod&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secret&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;as&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;volume
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pod&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secret&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;as&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;volume
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; keys
    &lt;span class=&quot;token key atrule&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;secretName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; keys&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secret
  &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pod&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secret&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;as&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;volume
    &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PRIVATE_KEY
      &lt;span class=&quot;token key atrule&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /etc/keys/key.pem
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PUBLIC_KEY
      &lt;span class=&quot;token key atrule&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /etc/keys/key.pub
    &lt;span class=&quot;token key atrule&quot;&gt;volumeMounts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; keys
      &lt;span class=&quot;token key atrule&quot;&gt;readOnly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/etc/keys&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Check the status of the pod.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl get pod pod-with-secret-as-volume&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The pod won&apos;t be created until the secret is present.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This can be altered using the &lt;code class=&quot;language-text&quot;&gt;optional: true&lt;/code&gt; option. If an optional Secret doesn&apos;t exist, Kubernetes ignores it. (&lt;a href=&quot;https://kubernetes.io/docs/concepts/configuration/secret/#restriction-secret-must-exist&quot;&gt;#restriction-secret-must-exist&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Create a secret with RSA keys&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;openssl genrsa &lt;span class=&quot;token parameter variable&quot;&gt;-out&lt;/span&gt; key.pem &lt;span class=&quot;token number&quot;&gt;2048&lt;/span&gt;
openssl rsa &lt;span class=&quot;token parameter variable&quot;&gt;-in&lt;/span&gt; key.pem &lt;span class=&quot;token parameter variable&quot;&gt;-pubout&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-out&lt;/span&gt; key.pub
kubectl create secret generic keys-secret --from-file&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;key.pem --from-file&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;key.pub&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Check the status of the pod.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl get pod pod-with-secret-as-volume
kubectl &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; pod-with-secret-as-volume -- &lt;span class=&quot;token function&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-al&lt;/span&gt; /etc/keys
kubectl &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; pod-with-secret-as-volume -- &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; KEY&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The expected result is:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;pod-with-secret-as-volume   &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/1     Running&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
lrwxrwxrwx &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root root   &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt; Feb &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;:26 key.pem -&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;data/key.pem
lrwxrwxrwx &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root root   &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt; Feb &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;:26 key.pub -&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;data/key.pub
&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token assign-left variable&quot;&gt;PRIVATE_KEY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/etc/keys/key.pem
&lt;span class=&quot;token assign-left variable&quot;&gt;PUBLIC_KEY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/etc/keys/key.pub&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;Delete the secret and observe the behavior of the pod.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl delete secret keys-secret
kubectl get pod pod-with-secret-as-volume
kubectl &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; pod-with-secret-as-volume -- &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; KEY&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The expected result is:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;pod-with-secret-as-volume   &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/1     Running&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token assign-left variable&quot;&gt;PRIVATE_KEY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/etc/keys/key.pem
&lt;span class=&quot;token assign-left variable&quot;&gt;PUBLIC_KEY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/etc/keys/key.pub&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When a secret is mounted as a volume, the pod retains the secret&apos;s data even if the secret is deleted. This means that the pod continues to function normally, oblivious to the absence of the secret. However, if the pod is deleted and recreated when the secret is not present, the pod will fail to start, as it will be unable to access the secret.&lt;/p&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;Check that files are still present in the pod.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; pod-with-secret-as-volume -- &lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; /etc/keys/key.pub&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The mounted files are still present in the pod.&lt;/p&gt;
&lt;p&gt;When to Use Secret as Volume:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;when your application requires access to the secret as a file&lt;/li&gt;
&lt;li&gt;when you want to mount all the keys from the secret as files&lt;/li&gt;
&lt;li&gt;when the new files should be automatically available in the containers after the new version of an application is deployed (POD is recreated automatically)&lt;/li&gt;
&lt;li&gt;when the pod&apos;s container is aware of the secret&apos;s files paths&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;secret-as-environment-variables-envfrom&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#secret-as-environment-variables-envfrom&quot; aria-label=&quot;secret as environment variables envfrom permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Secret as Environment Variables (envFrom)&lt;/h2&gt;
&lt;p&gt;Another method for consuming secrets in Kubernetes is by injecting them as environment variables using the &lt;code class=&quot;language-text&quot;&gt;envFrom&lt;/code&gt; field in the pod specification. This approach is suitable when your application requires access to the secret as environment variables.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a pod with the secret injected as environment variables.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Pod
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pod&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secret&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;from
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pod&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secret&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;from
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pod&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secret&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;from
    &lt;span class=&quot;token key atrule&quot;&gt;envFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;secretRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; db&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secret&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Check the status of the pod.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl get pod pod-with-secret-env-from&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The pod will not be created until the secret is present.&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Create a secret with a database credentials.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl create secret generic db-secret --from-literal&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;DB_ADMIN&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myadmin --from-literal&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;DB_PASSWORD&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;mypass&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Check the pod&apos;s details.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl get pod pod-with-secret-env-from
kubectl describe pod pod-with-secret-env-from &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-A&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Environment&quot;&lt;/span&gt;
kubectl &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; pod-with-secret-env-from  -- &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; DB_&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The result should be:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;pod-with-secret-env-from   &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/1     Running
&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
    Environment Variables from:
      db-secret   Secret  Optional: &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    Environment:  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;none&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-lbrbb &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ro&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token assign-left variable&quot;&gt;DB_ADMIN&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myadmin
&lt;span class=&quot;token assign-left variable&quot;&gt;DB_PASSWORD&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;mypass&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;Delete the secret and observe the behavior of the pod.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl delete secret db-secret
kubectl get pod pod-with-secret-env-from
kubectl &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; pod-with-secret-env-from  -- &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; DB_&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The result should be:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;pod-with-secret-env-from   &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/1     Running
&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token assign-left variable&quot;&gt;DB_ADMIN&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myadmin
&lt;span class=&quot;token assign-left variable&quot;&gt;DB_PASSWORD&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;mypass&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As observed in this scenario, the pod is not recreated when the secret is deleted and environment variables loaded during the pod&apos;s creation still persist.&lt;/p&gt;
&lt;ol start=&quot;7&quot;&gt;
&lt;li&gt;Recreate the secret with a new database password and a new key-value pair.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl create secret generic db-secret &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
--from-literal&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;DB_ADMIN&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myadmin &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
--from-literal&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;DB_PASSWORD&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;mynewpass &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
--from-literal&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;DB_URL&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;mydb&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;8&quot;&gt;
&lt;li&gt;View the environment variables in the pod.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; pod-with-secret-env-from  -- &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; DB_&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The result should be:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;DB_ADMIN&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myadmin
&lt;span class=&quot;token assign-left variable&quot;&gt;DB_PASSWORD&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;mypass&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once the secret is created again and the pod is not recreated, then the environment variables are not refreshed.&lt;/p&gt;
&lt;p&gt;When to Use Secret as Environment Variables (envFrom):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;when your application requires access to the secret as environment variables&lt;/li&gt;
&lt;li&gt;when we want to load all the key-value pairs from the secret as environment variables&lt;/li&gt;
&lt;li&gt;when the new environment variables should be automatically available in the containers when pod is recreated (pod recreaction can be triggered automatically by using Helm charts, when secret&apos;s hash is changed)&lt;/li&gt;
&lt;li&gt;when we expect the pod to be created even when the secret will not contain the required keys&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;secret-as-environment-variables-secretkeyref&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#secret-as-environment-variables-secretkeyref&quot; aria-label=&quot;secret as environment variables secretkeyref permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Secret as Environment Variables (secretKeyRef)&lt;/h2&gt;
&lt;p&gt;The final method for consuming secrets in Kubernetes is by injecting them as environment variables using the &lt;code class=&quot;language-text&quot;&gt;secretKeyRef&lt;/code&gt; field in the pod specification. This approach is suitable when your application requires access to specific keys from the secret as environment variables.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a pod with the secret injected as environment variables using &lt;code class=&quot;language-text&quot;&gt;secretKeyRef&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Pod
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pod&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secret&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secret&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;ref
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pod&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secret&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secret&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;ref
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx
      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pod&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secret&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secret&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;ref
      &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; APP_PREFIX_DB_PASS
        &lt;span class=&quot;token key atrule&quot;&gt;valueFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;secretKeyRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; db&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secret
            &lt;span class=&quot;token key atrule&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; DB_PASSWORD&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Check the status of the pod.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl get pod pod-with-secret-env-secret-key-ref&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The pod will not be created until the secret is present.&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Create a secret with a database credentials.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl create secret generic db-secret --from-literal&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;DB_ADMIN&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myadmin --from-literal&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;DB_PASSWORD&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;mypass&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Check the status of the pod.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl get pod pod-with-secret-env-secret-key-ref
kubectl &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; pod-with-secret-env-secret-key-ref  -- &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; APP_PREFIX_&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The expected result is:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;pod-with-secret-env-secret-key-ref   &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/1     Running&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token assign-left variable&quot;&gt;APP_PREFIX_DB_PASS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;mypass&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;Delete the secret and observe the behavior of the pod.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl delete secret db-secret
kubectl get pod pod-with-secret-env-secret-key-ref
kubectl &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; pod-with-secret-env-secret-key-ref  -- &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; APP_PREFIX&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The expected result is:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;pod-with-secret-env-secret-key-ref   &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/1     Running&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token assign-left variable&quot;&gt;APP_PREFIX_DB_PASS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;mypass&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;Recreate the secret with a new database password.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl create secret generic db-secret --from-literal&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;DB_ADMIN&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;myadmin --from-literal&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;DB_PASSWORD&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;mynewpass&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;7&quot;&gt;
&lt;li&gt;View the environment variables in the pod.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kubectl &lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; pod-with-secret-env-secret-key-ref  -- &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; APP_PREFIX&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The result should be:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;APP_PREFIX_DB_PASS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;mypass&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As observed in this scenario, the pod is not affected if the secret is deleted. Container environment variables are not updated when the secret is recreated.&lt;/p&gt;
&lt;p&gt;When to Use Secret as Environment Variables (secretKeyRef):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;when the secret&apos;s data key is different than the one required by the application&lt;/li&gt;
&lt;li&gt;when your application requires access to specific keys from the secret as environment variables (more control over the keys, more secure)&lt;/li&gt;
&lt;li&gt;when the pod should not be created if the specific key is not present in the secret during the pod&apos;s creation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The difference between the &lt;code class=&quot;language-text&quot;&gt;envFrom&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;secretKeyRef&lt;/code&gt; methods is that the &lt;code class=&quot;language-text&quot;&gt;envFrom&lt;/code&gt; method loads all the key-value pairs from the secret as environment variables, while the &lt;code class=&quot;language-text&quot;&gt;secretKeyRef&lt;/code&gt; method allows you to specify the keys from the secret that should be injected as environment variables. With the default settings, &lt;strong&gt;if the key is not present in the secret, the pod will not be created (during pod creation time).&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;The consumption of secrets in Kubernetes pods and containers in all scenarios above follows similar behavior patterns.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;by default, the pod is not created until the secret is present&lt;/li&gt;
&lt;li&gt;the pod keeps the secret&apos;s data even if the secret is deleted&lt;/li&gt;
&lt;li&gt;the pod is not recreated when the secret is deleted&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key differences among the secret consumption options are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the method of accessing the secret&apos;s data (as files or environment variables)&lt;/li&gt;
&lt;li&gt;the level of control over the secret&apos;s keys&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Summarizing, the secret as a volume is suitable when your application requires access to the secret as a file, while the secret as environment variables (envFrom and secretKeyRef) is suitable when your application requires access to the secret as environment variables. The &lt;code class=&quot;language-text&quot;&gt;envFrom&lt;/code&gt; method loads all the key-value pairs from the secret as environment variables, while the &lt;code class=&quot;language-text&quot;&gt;secretKeyRef&lt;/code&gt; method allows you to specify the keys from the secret that should be injected as environment variables.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to manage your time as a software engineer - 5 practical tips and tools]]></title><description><![CDATA[In this post, we will share 5 practical tips and tools that help us manage time and keep our productivity high. Spoiler alert: there will be…]]></description><link>https://handsonarchitects.com/blog/2024/how-to-manage-your-time-as-software-engineer-5-practial-tips-and-tools/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2024/how-to-manage-your-time-as-software-engineer-5-practial-tips-and-tools/</guid><pubDate>Tue, 30 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/01d160b0f7bc9b3b36a5ede7076f9e37/e5715/software-engineer-manages-time.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAElklEQVR42iWUe0zTVxTHm2wzZm7GJSC6P/bQEHA8BAQik7DNuE3qKAUstJQWKPB7VhBKBcoYUJE3pVCg/T36ooI8ZEZwowiiIAriNMw5MkfbXwvloUyXbMmWZcnY/UFycnNvcj45537v+V4ORjII6URIBibdCOUGG4RwYJQbJ7357SsF7W6cXkdoL0R5YXoVNrKBGlfYfMLFAXmQwQEbHBi9ghAu1OBELOtow7Ta1CmZJ+BxbU2pHK6dlLfdxZrvIHW3cqvtstopUAYm3ByEdMOkS25eRQ2OvJbZ8OgEYf2sXDWk75Ql/VGS7IRqhAdJDTFqu9TXXGL5Gjeq5GqJSNr4AKWXObDBiZtWBArbgXf8/Pbs3b/rjZSqm4WVA5N6ybnbijyKqxMdtV0Z67l+nzBeGxyZNn//sPJ8eVbdDGbycmCSAW0H+IcKIn3y4nxT0iHI9md+AUHMFaPM1ZRFhR6L6O2+fqFKNzH54N6jxWK1plZVLWt5DG7OQWkP0rVYnHn6h5m+dafd0n8zx/hSAGnVBSeElsLISzF41Ie0ucfac2Vx4enikwWTrkmjUGa3PoEpDxCMgejV0uqOv55ptv6+89DeJ6kZq6lvVZdpk7K10pJBQaVVXHlVrh2RVpGIwlJYbs4pIFH9EkQ4gWAu3LwmanpKlJ/67Reqoc3Qpfi4VZXX2GHN1i0VGcdV3zbwS2hhf1Nkq0TSOJd6rzmxhcT1XphysWqjpBOiPGeVg5rkkCHcn6nw0YsOV5c2IwP/CXgZ3NAwSPsjL/RwQlqxrHIoMWxfalm/3LwOg8ooyaDkUjaxqrUM//tzu2OgbA7bMyDz01Sp5LQbUt+QNd6FSQ8PJYW1s7l109JzBjAOO0MFYDdGOSSdG3Z92tZo8O9zyonygNuZPue1o+nqMTF+UaL5Cf/GRF3utfaa8ut706iXGL2MkizPwijFSDtXpto+2WrgrOm596Ej7coyvMQyIjxkTw8cTvWlakSb3nHv2sR3F0WDXH9pCprLUqAy4cJoRtrhmaiL2prat8UMNxXZxKZNWIgrA1+/FLa3ONxX8Gk0LDwj5n+Jh76rfIvD/SIbsT5HWLUJB250iduWb+niXtkjunUtioRM2LiWXjESH/JR/LFjvIjw5KNB8UcCE4IDow/4fL6Lw+djLGxwALWdGOWUGdbqa8sE0T4+u99+703OaWGZsNiWFvqB5HiYOCY8/URURmx05snYrwLfT36Nc4YLI9YXiAGoTbHmwmhPDrWR3rggQLRcblYKrpeWDyYHH0qODOdHhPLDg/gh/klhAZ/57o4/6Css7EaNa6xg265i4K5nWfXTZxW2uJOCU4kYL68VaXuc0zAjrZnMqLJLKm5kqK6JlD2p8i6Z5hFmeQ4B54KngncejXDFcZHjMbwEtDMoJJYHtQgu9Mttm4hpFTOtY5YNzPJC3r2JX34FzATMv025WBgcUKNX1jyf2zRfNPCPuHoceBUEbFiC9EuI3gHpfwV7EHldYHWyCPg2KPYzAGUZ2OBCKACsoAQDRh0i3RABrrO8vXrY7J00chkhPaBhQKEU8z/FgnsuQi5nDgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Software engineer manages time&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/01d160b0f7bc9b3b36a5ede7076f9e37/e5715/software-engineer-manages-time.png&quot;
        srcset=&quot;/static/01d160b0f7bc9b3b36a5ede7076f9e37/ff46a/software-engineer-manages-time.png 325w,
/static/01d160b0f7bc9b3b36a5ede7076f9e37/a6d36/software-engineer-manages-time.png 650w,
/static/01d160b0f7bc9b3b36a5ede7076f9e37/e5715/software-engineer-manages-time.png 768w&quot;
        sizes=&quot;(max-width: 768px) 100vw, 768px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In this post, we will share 5 practical tips and tools that help us manage time and keep our productivity high.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Spoiler alert: there will be no breakthroughs and no silver bullets in this post. You probably know or at least heard of the techniques and tools we will describe. In this post, we want to add a software engineering perspective to the time management topic  and share some of the tools we use to manage our time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;1-deep-focus-and-pomodoro-techniques&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-deep-focus-and-pomodoro-techniques&quot; aria-label=&quot;1 deep focus and pomodoro techniques permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Deep focus and Pomodoro techniques&lt;/h2&gt;
&lt;p&gt;Working as a software engineer taught us that there is no silver bullet when it comes to productivity. Some days, you have many small tasks you need to complete, and some days, you have a single big task you need to focus on.&lt;/p&gt;
&lt;p&gt;Depending on the type of our daily plan, we use different techniques and tools to help us stay focused and productive.&lt;/p&gt;
&lt;p&gt;When we have many small tasks that we need to complete (e.g., writing documentation, reviewing pull requests, red-green-refactor coding of already designed applications, or writing an architecture decision record), we use the &lt;code class=&quot;language-text&quot;&gt;Pomodoro&lt;/code&gt; technique. This technique uses a timer to break down work into intervals, traditionally 25 minutes in length (however, depending on the type of tasks, we make it up to 55 min per task in a row), separated by short breaks. It is a great way to stay focused and get many small things done. Pomodoro also supports pair programming sessions, helping us switch between driver and navigator roles at set intervals.&lt;/p&gt;
&lt;p&gt;However, the Pomodoro technique is not the best way to get into the flow state (e.g., designing/coding a more demanding module, reverse engineering a system that needs refactoring, writing a blog post, preparing a presentation). These activities require &quot;loading everything to your cache&quot; to understand or present the problem. In these cases, we try to get into a &lt;code class=&quot;language-text&quot;&gt;deep focus&lt;/code&gt;, which is nothing else than giving all your brain power to a single but very complex task for a longer period. There is no rule on how long the deep focus session should last. It depends on the task, the person, the mood, and many other factors. However, there are two rules that we always try to follow: &lt;strong&gt;limit distractions&lt;/strong&gt; and &lt;strong&gt;take breaks&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tooling&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For the Pomodoro technique, thanks to its simplicity, all you need is a timer. We use the following tools:
&lt;ul&gt;
&lt;li&gt;Physical timer (our favorite one)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pomofocus.io/&quot;&gt;Pomodoro Timer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://xwavesoft.com/be-focused-pro-for-iphone-ipad-mac-os-x.html&quot;&gt;Be Focused&lt;/a&gt; (macOS app)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;While the deep focus technique we tend to limit the tooling to the minimum. Read the following sections to learn more about it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;2-turn-off-notifications&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-turn-off-notifications&quot; aria-label=&quot;2 turn off notifications permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Turn off notifications&lt;/h2&gt;
&lt;p&gt;Limiting distractions helps not only during the deep focus but also during the Pomodoro sessions. Of course we don&apos;t recommend ignoring your teammates for the whole day. Instead, we recommend planning your day with a few hours of uninterrupted time. One of the techniques we use in the teams we work with is so-called &lt;code class=&quot;language-text&quot;&gt;focus hours&lt;/code&gt;. As a team, we agree on a few (usually 2-3) hours during the day when we don&apos;t plan meetings and do not expect quick responses to our messages. This approach helps especially with deep focus sessions. When we work most of the day with the Pomodoro technique, we plan a session for communication every 2-3 Pomodoros. This way we can focus on the task at hand and still be available for our teammates.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tooling&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;macOS &lt;a href=&quot;https://support.apple.com/guide/mac-help/turn-a-focus-on-or-off-mchl999b7c1a/mac&quot;&gt;Do Not Disturb&lt;/a&gt; feature&lt;/li&gt;
&lt;li&gt;Slack &lt;a href=&quot;https://slack.com/help/articles/214908388-Pause-notifications-with-Do-Not-Disturb&quot;&gt;pause notifications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Calendar &lt;code class=&quot;language-text&quot;&gt;focus time&lt;/code&gt; event (various calendars support this feature, e.g. &lt;a href=&quot;https://support.google.com/calendar/answer/11190973?hl=en&amp;#x26;co=GENIE.Platform%3DDesktop&quot;&gt;Google Calendar&lt;/a&gt; or &lt;a href=&quot;https://answers.microsoft.com/en-us/msteams/forum/all/focus-time-microsoft/d095b0bf-f5ea-4301-89f6-c79c5db9d02e&quot;&gt;Microsoft Teams&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;There are also hardware solutions that can help you limit distractions and communicate your availability to your teammates. Just as an example, look at the &lt;a href=&quot;https://www.amazon.com/s?k=busy+light&quot;&gt;busy light Amazon search results&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;3-take-breaks-and-drink-water&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-take-breaks-and-drink-water&quot; aria-label=&quot;3 take breaks and drink water permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Take breaks and drink water&lt;/h2&gt;
&lt;p&gt;We know it might sound cliché, but we tried both approaches with or without regular breaks. We can tell you that taking breaks is a game-changer for keeping your productivity high throughout the day.&lt;/p&gt;
&lt;p&gt;During the Pomodoro we usually take a break after every session. We use this time to stand up, stretch, change the desk&apos;s position (from sitting to standing or back), drink coffee, and talk to our teammates. This way we get back to the next session with a fresh mind, ready to switch context and start working on a new task.&lt;/p&gt;
&lt;p&gt;During the deep focus sessions, breaks are less frequent but even more important (unless we want to end our working day as a walking zombie). We noticed that taking breaks that change the environment (e.g., going for a walk) helps us to get back to the task with a fresh mind, new ideas, and most often new perspective on the problem at hand (but this is a material for a separate blog post). Also, don&apos;t forget to have a glass of water on your desk. We know that might sound simple, but we noticed that staying hydrated helps us stay focused and productive for a longer time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tooling&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A simple timer will do the job.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;4-context-switching-is-expensive-avoid-it-and-use-the-right-tools&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-context-switching-is-expensive-avoid-it-and-use-the-right-tools&quot; aria-label=&quot;4 context switching is expensive avoid it and use the right tools permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. Context switching is expensive, avoid it and use the right tools&lt;/h2&gt;
&lt;p&gt;This section will focus on the tools and techniques, as we believe there is consensus that context switching is expensive and should be avoided.&lt;/p&gt;
&lt;p&gt;One of the techniques that we use to avoid unwanted context-switching is to plan our day. We use the &lt;a href=&quot;https://gettingthingsdone.com/&quot;&gt;Getting Things Done&lt;/a&gt; (GTD) approach to plan for it (however, we modified it a bit to fit our needs). In short, GTD is a methodology for organizing tasks into actionable items (it uses of the divide-and-conquer approach). One of the greatest benefits we like in GTD is that it makes more space in our &quot;RAM&quot; (brain) for the actual work. We don&apos;t need to remember what we need to do. We just need to check our GTD board and pick the next task. This way, we can focus on the task at hand and not worry about forgetting something. Here&apos;s a sample board that we use for managing our tasks (and yes, this probably looks familiar to you):&lt;/p&gt;
&lt;table border=&quot;1&quot;&gt;
  &lt;tr&gt;
    &lt;th&gt;Inbox&lt;/th&gt;
    &lt;th&gt;Now&lt;/th&gt;
    &lt;th&gt;Next&lt;/th&gt;
    &lt;th&gt;Waiting for&lt;/th&gt;
    &lt;th&gt;Done&lt;/th&gt;
    &lt;th&gt;Backlog/Someday&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;&amp;nbsp;&lt;/td&gt;
    &lt;td&gt;&lt;/td&gt;
    &lt;td&gt;&lt;/td&gt;
    &lt;td&gt;&lt;/td&gt;
    &lt;td&gt;&lt;/td&gt;
    &lt;td&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;We recommend reading the &lt;a href=&quot;https://www.amazon.com/Getting-Things-Done-Stress-Free-Productivity/dp/0143126563&quot;&gt;GTD book&lt;/a&gt; to learn more about the approach, but in a nutshell, the board above works the following way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Inbox&lt;/code&gt; is our &quot;brain dump&quot; place. Whenever we think of something we need to do, we put it in the inbox. We don&apos;t worry about the task&apos;s priority or complexity at this stage. Only two things matter: &lt;strong&gt;don&apos;t worry about this task any more&lt;/strong&gt; and &lt;strong&gt;don&apos;t forget about it&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Now&lt;/code&gt; is where we put tasks that we want to complete during the next iteration (e.g., today, this week, sprint, etc.). We try to limit the number of tasks in this column so that we can focus on the most important ones.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Next&lt;/code&gt; is the place where we put candidates for the next iteration. Most often they need some refinement before we can move them to the Now column.&lt;/li&gt;
&lt;li&gt;In &lt;code class=&quot;language-text&quot;&gt;Waiting for&lt;/code&gt; we put in here tasks that require some external action before we can complete them. For example, we put tasks that require a response from a customer or teammate in this column.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Done&lt;/code&gt; is the place where we put tasks that we completed. We use this column to track our progress and to have a sense of accomplishment. We noticed that cleaning this column gives us a lot of satisfaction ;).&lt;/li&gt;
&lt;li&gt;Finally, &lt;code class=&quot;language-text&quot;&gt;Backlog/Someday&lt;/code&gt; is the place where we put tasks that we want to do in the future but not now. We use this column to keep our &lt;code class=&quot;language-text&quot;&gt;Inbox&lt;/code&gt; clean and to avoid forgetting about activities we want to do in the future. We review it regularly to make sure that we don&apos;t miss anything important but also to remove tasks that are not relevant anymore.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, some of you might ask how the GTD board complies with Jira or whatever tool your team uses to manage project. We treat GTD as a tool at a personal level. We often work on multiple projects at the same time, so we need a tool that helps us manage our time and tasks across and between projects. We use the GTD board to manage our time and tasks. We use the team&apos;s tooling to manage the project tasks. We found this approach to be the most effective for us. Also, ask yourself how many times there was an activity you had to do that was not worth putting in the team&apos;s Jira. In these cases, we have a go-to place to put these tasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tooling&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://trello.com/&quot;&gt;Trello&lt;/a&gt; - for managing GTD tasks. It is so far the best tool we found because of its &lt;a href=&quot;https://trello.com/guide/automate-anything&quot;&gt;automation capabilities&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;5-when-you-dont-know-how-timebox-it&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-when-you-dont-know-how-timebox-it&quot; aria-label=&quot;5 when you dont know how timebox it permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. When you don&apos;t know how, timebox it&lt;/h2&gt;
&lt;p&gt;The last one might also sound like a slogan (&lt;em&gt;hey, we didn&apos;t promise breakthroughs, remember?&lt;/em&gt;), but we found it useful to keep tracking the time we spend on tasks. There are no rules on how long the timebox should be. The rule here is once the time is over, you need to decide. Either you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;know enough&lt;/code&gt; and finish the timebox,&lt;/li&gt;
&lt;li&gt;you &lt;code class=&quot;language-text&quot;&gt;don&apos;t know enough&lt;/code&gt;, and you abort the task or find alternative solutions,&lt;/li&gt;
&lt;li&gt;or you &lt;code class=&quot;language-text&quot;&gt;extend the timebox&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Either of the outcomes is fine. The important thing is that you don&apos;t spend too much time on a single task if you don&apos;t believe that you are making progress or it is important enough to spend more time on it. Sometimes, we decide to put the task back to &lt;code class=&quot;language-text&quot;&gt;Someday&lt;/code&gt; and get back to it later when we know more about the problem or when we have more time to spend on it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tooling&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Yes, you guessed it right - a timer and a Trello displaying other tasks we need to complete are enough.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We presented five techniques and tools that help us thrive as software engineers and keep our productivity stable and at a high level. We hope you find them useful. If you have any questions or comments, please let us know using social media.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Multi-component Helm chart labeling and naming]]></title><description><![CDATA[In the world of Kubernetes, Helm charts are a powerful tool for deploying and managing applications. When you create a new chart using the…]]></description><link>https://handsonarchitects.com/blog/2023/multi-component-helm-chart-labeling-and-naming/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2023/multi-component-helm-chart-labeling-and-naming/</guid><pubDate>Tue, 19 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/d60216cba360c31f0e327a39dc53f19c/e5715/multi-component-chart.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEmElEQVR42m1Ue0xTdxQ+vbfISAZzJgyJxm0BJ4gONoggbjwmmw+cYsaGqGG4gChDHhJaXn0I5aGkvEozvOVVWiiFPi+FFuiLW1psKchDp8PHnNlcTDRL5jSOdnS3c/tvJ98fJye/7zsnOd/vgC8+/bpMj44YQGGGUSuoLaAiQDXtBW7x4lXuLf5TJx8ozf8mifIpu/260LGcqp3Z0jUEcgMFt1CUJHMGVRGozEgqIl7CNKIyIyRNSfxHJiBQqZcL2ffmJm8rhJnNPJAYEbkVVRMUtdn/qpTa0Q/SCRgxgZqUc3hB0sgZX6mAeCJU2s/7Nvx0VfbOrn6cGeFkBB+vLQS1480hrWBuyXH7zhHcAPjcVxg9jc+AUSeiNFIUJu90kULBwZzTIdw2wOcRqUFSl/CSDr0XI0Bjix3WLRC6eyszAzIJqOdb6IlftlbAxA0Ut4JmDjQOONzH7cvcQlXZfOQGf67Av6ErkCekV7J3t+lDhhSr09hTQ3l9UbQ/ps4ot27snaLK9YF90gg+b2cnH0C7YG6PO9bBAt0KilvQ4SmQjAX3XMurfZSUL+46u5mgBSUyi1OurBxgO0Mx/kxJ8K8M33ulfi+ZVACp9ZvuKklDIuCLG9SEn0IPY1YYNW/unkqv/f7kxbFhuSK/nshgLCP9+r1Yi7sanrM3rF2iutg+8EZdR4DKYr4ctWNwOLq19QQt17sYEhqCKtOk1ljKWx/nXnoQgmlh0r5ROmEufNtTAX+xKB4mAKqZhfGVEhGNT4sLau2O7+sJ6xPCuAOVm2Dcuo9ryWl8ll3/JKyDXLgJNPPJgva1KtTDgnUWQKi4GxSm3X0C1dmg+JqqHZz6GCaTNBNKbnLcGMlV5FYsnK/9MbpFC2ojqjKD4toMJ8LDADcDAV1WaNmhtPZjydgnYZFF9KgmTnQ7F8ZIPxAhmO0k2xnVOJh2xXmKc5M6rPPRkA61i+oSPJXgZqGgzAyzZ23vSUmofn93QWwc6+iRvZW1PqLRINFgQ2HJjpYh0Fk3icdz2Tf3N6l9JBqQzZpqwsnOf1ahwEgNG01HNNlhI1k7uw+Hig/E8GM+5ETtOnsgrTdrTyy7EiaXKYZ5X9lcOq1MULCluTLpRQXVw6R42BS4e/8Rp7i0eM92XspW3ZnQ7C8+Pkhvbh5QsVqEeanF+QkZeae/PlJUuqu68VBRzkSW7+MyxNNAMsGe7w/rHm/MO5cul5TlfxSzbU/iB9yBeOcvKQ9dJ0fG8PzPlAnRovh4e3r4rYKApQt+v9HBlkW9mvdeCL8TXG63y+1a93hF7v/0s3pAXHT+wqHMM5EnzqWnxQk/h7b9AcykzfQEKieN0nJq27mjSTQa69PBCdDOg+f/Yu3Z7/evO28YDboRtXZANDkomsIVNrvDYL/huPsQW1x9Z5Q8Emb44+kTpXVWPm1bf/Fcb7NOzdpvPXgoMRFul0v/wx3D4pLq5mqO1mK580BBXMvo7E3GRHGDY4jc6HVhfI/iLf5Q4GXseHvvVoFsUz3/3SbsNZ54H/e7YGyY2iz0axOCfBrpxRHeAJDTkv/Re5XIzpa/Ae+wZa6ogfUcAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;multi-component chart&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/d60216cba360c31f0e327a39dc53f19c/e5715/multi-component-chart.png&quot;
        srcset=&quot;/static/d60216cba360c31f0e327a39dc53f19c/ff46a/multi-component-chart.png 325w,
/static/d60216cba360c31f0e327a39dc53f19c/a6d36/multi-component-chart.png 650w,
/static/d60216cba360c31f0e327a39dc53f19c/e5715/multi-component-chart.png 768w&quot;
        sizes=&quot;(max-width: 768px) 100vw, 768px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In the world of Kubernetes, Helm charts are a powerful tool for deploying and managing applications. When you create a new chart using the &lt;code class=&quot;language-text&quot;&gt;helm create&lt;/code&gt; command, one of the artifacts generated is a set of helper functions in a file named &lt;code class=&quot;language-text&quot;&gt;templates/_helpers.tpl&lt;/code&gt;. These functions are designed with single-component charts in mind. But what happens when your application isn&apos;t so straightforward? What if your chart consists of multiple components, such as a frontend, backend, database, and cache?&lt;/p&gt;
&lt;p&gt;The Helm documentation provides some guidance on this, suggesting the use of the &lt;code class=&quot;language-text&quot;&gt;app.kubernetes.io/component&lt;/code&gt; label for multi-component charts. However, it doesn&apos;t go into detail on how to implement these labels. In fact, a search for examples and best practices on this topic yields surprisingly few results. That&apos;s why I&apos;ve decided to share my approach to this problem. In this post, I&apos;ll provide an example of a multi-component helper function for Helm charts, and explain how you can use it in your own projects.&lt;/p&gt;
&lt;h2 id=&quot;example-helper-methods&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#example-helper-methods&quot; aria-label=&quot;example helper methods permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Example helper methods&lt;/h2&gt;
&lt;p&gt;Going straight to the code, here&apos;s an example of a helper function that generates labels for a multi-component chart:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/*
Create a default fully qualified component name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
Usage:
{{ include &quot;my-chart.component.fullname&quot; (dict &quot;componentName&quot; &quot;component-name&quot; &quot;context&quot; $) }}
*/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; define &lt;span class=&quot;token string&quot;&gt;&quot;my-chart.component.fullname&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Values&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fullnameOverride &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; printf &lt;span class=&quot;token string&quot;&gt;&quot;%s-%s&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Values&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fullnameOverride &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;componentName &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; trunc &lt;span class=&quot;token number&quot;&gt;63&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; trimSuffix &lt;span class=&quot;token string&quot;&gt;&quot;-&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; $name &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Chart&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Values&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nameOverride &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; contains $name &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Release&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; printf &lt;span class=&quot;token string&quot;&gt;&quot;%s-%s&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Release&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;componentName &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; trunc &lt;span class=&quot;token number&quot;&gt;63&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; trimSuffix &lt;span class=&quot;token string&quot;&gt;&quot;-&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; printf &lt;span class=&quot;token string&quot;&gt;&quot;%s-%s-%s&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Release&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name $name &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;componentName &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; trunc &lt;span class=&quot;token number&quot;&gt;63&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; trimSuffix &lt;span class=&quot;token string&quot;&gt;&quot;-&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; end &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; end &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; end &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/*
Metadata labels for chart component
Usage:
{{ include &quot;my-chart.component.labels&quot; (dict &quot;componentName&quot; &quot;component-name&quot; &quot;context&quot; $) }}
*/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; define &lt;span class=&quot;token string&quot;&gt;&quot;my-chart.component.labels&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
helm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sh&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;chart&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; include &lt;span class=&quot;token string&quot;&gt;&quot;my-chart.chart&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;context &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; include &lt;span class=&quot;token string&quot;&gt;&quot;my-chart.component.selectorLabels&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dict &lt;span class=&quot;token string&quot;&gt;&quot;componentName&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;componentName &lt;span class=&quot;token string&quot;&gt;&quot;context&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Chart&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AppVersion &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;kubernetes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;version&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Chart&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AppVersion &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; quote &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; end &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;kubernetes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;managed&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Release&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Service &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; end &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/*
Selector labels for chart component
Usage:
{{ include &quot;my-chart.component.selectorLabels&quot; (dict &quot;componentName&quot; &quot;component-name&quot; &quot;context&quot; $) }}
*/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; define &lt;span class=&quot;token string&quot;&gt;&quot;my-chart.component.selectorLabels&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; include &lt;span class=&quot;token string&quot;&gt;&quot;my-chart.selectorLabels&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;context &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;kubernetes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;io&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;component&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;componentName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; end &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, you can simply include this helper function in your chart&apos;s &lt;code class=&quot;language-text&quot;&gt;templates/_helpers.tpl&lt;/code&gt; file, and use it to generate labels for each component in your chart. For example, here&apos;s how you might use it to generate labels for a frontend component:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deployment
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; include &quot;my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;chart.component.fullname&quot; (dict &quot;componentName&quot; &quot;frontend&quot; &quot;context&quot; $) &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; include &quot;my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;chart.component.labels&quot; (dict &quot;componentName&quot; &quot;frontend&quot; &quot;context&quot; $) &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; nindent 4 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; include &quot;my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;chart.component.selectorLabels&quot; (dict &quot;componentName&quot; &quot;frontend&quot; &quot;context&quot; $) &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; nindent 6 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; include &quot;my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;chart.component.labels&quot; (dict &quot;componentName&quot; &quot;frontend&quot; &quot;context&quot; $) &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; nindent 8 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; frontend
          &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here is the output created by the &lt;code class=&quot;language-text&quot;&gt;helm template&lt;/code&gt; command:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Source: my-chart/templates/frontend-deployment.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deployment
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;chart&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;frontend
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;helm.sh/chart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;chart&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;1.0.0
    &lt;span class=&quot;token key atrule&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;chart
    &lt;span class=&quot;token key atrule&quot;&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;chart
    &lt;span class=&quot;token key atrule&quot;&gt;app.kubernetes.io/component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; frontend
    &lt;span class=&quot;token key atrule&quot;&gt;app.kubernetes.io/version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.1.0&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;app.kubernetes.io/managed-by&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Helm
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;chart
      &lt;span class=&quot;token key atrule&quot;&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;chart
      &lt;span class=&quot;token key atrule&quot;&gt;app.kubernetes.io/component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; frontend
  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;helm.sh/chart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;chart&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;1.0.0
        &lt;span class=&quot;token key atrule&quot;&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;chart
        &lt;span class=&quot;token key atrule&quot;&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;chart
        &lt;span class=&quot;token key atrule&quot;&gt;app.kubernetes.io/component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; frontend
        &lt;span class=&quot;token key atrule&quot;&gt;app.kubernetes.io/version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.1.0&quot;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;app.kubernetes.io/managed-by&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Helm
    &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; frontend
          &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;how-it-works&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-it-works&quot; aria-label=&quot;how it works permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How it works&lt;/h2&gt;
&lt;p&gt;In short words, most of the work is done by the default helper function &lt;code class=&quot;language-text&quot;&gt;selectorLabels&lt;/code&gt;. Also, the default behavior of &lt;code class=&quot;language-text&quot;&gt;fullname&lt;/code&gt; function is preserved (when the Chart name is the same as the Release name, the Chart name is used as the full name). The only thing that is added is the &lt;code class=&quot;language-text&quot;&gt;componentName&lt;/code&gt; parameter, which is used to generate the &lt;code class=&quot;language-text&quot;&gt;app.kubernetes.io/component&lt;/code&gt; label and component&apos;s name postfix.&lt;/p&gt;
&lt;p&gt;If you have any questions or suggestions, feel free to leave a comment on social media or send me an email.&lt;/p&gt;
&lt;p&gt;Happy helming!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Manage complexity for demonstration environments]]></title><description><![CDATA[Dev/prod parity is one of the twelve-factor app principles. It clearly encourages us to “Keep development, staging, and production as…]]></description><link>https://handsonarchitects.com/blog/2023/manage-complexity-for-demonstration-environments/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2023/manage-complexity-for-demonstration-environments/</guid><pubDate>Wed, 08 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 512px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/6aa3764e7fea02f8c9bca20c5ff69566/01e7c/riding-backwards-on-rope.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEpUlEQVR42g2S2XMadQDHf6MdfXGs4zjT1hkdH6q1U7UzdmKnh3ZsU9vYNjGkaZM0RchBExI2heVa2OVYzl1guZYzC9kYYCFAOJMCOQhHLxN7jMeoL/4r5vn79Pl8vgBMvzw8mtXOCWlcRmDQ2MP+hXk+pZWQyByuhHCF2CIVZLzKEg2XA7LmitGP8XlXTxdjxnIEAUD04tRMirFCK5TWZoCm5q4VAnqGgLM+JB/SZ/1aNzavmuXT2FQ1oq2zRpZC+LcuaGWziwQGwNRvE4irFoC6nJm2Q7hFtr6Z/v2f14+K8RUnXGdNzWVd1q80wDM2pcSPqykjIn/AP/PlF73fXQVA/ArD9GGj2CSbvHlnuOCZf5nUsKTCESE4D1xl8FW/OknBGa+iEsVaGVeFMe4VPT4t//aVk+Dt+bZCPO3SimmLTCC4V9Hf/TU4gw31UErhY87Wydi7q876Er4ew0vRAwpN2itvc7bnpQABD4PDxtecW3Ewc1FdxAERov7YzPU16FbC9SAVkO5ydCvn3UwQe0Xv0xy5FlBuMGgtju2VfQbJbXCc+TvjgRusibGKE7SmQknD0nNV8qwYfWdc+4aVupILWypL5s2kfYdz5oPIOoNWoppHrP7nwQvgBhbZYIzFRT0ycT0027uB9ocVP/HkRwQEuCoGiPqTfb+4lSbaGbIYMT6K6RosVl/S54NKGh0DvUJlN2WL+1SJgHp72VyJoH+UAu0cPu987zoKRuZ6XsVVW1FVZ81bY40tzlaL6xpLuvqyqRBcAOcl7oBF6nEu7Gbdz4veMmsdthCzdBzC7w7BgDf9EakWkVOX65x3LaRpcZZmytZZJXfTJIWMgo9FlBCCC3HsVZX+byscTEZAP3To4vBbvLmjX3165LOTbpNyI4p1c1Q+oC7Q8u2U/Wk5ssFaLNJR8DUS6xubTHtkd4QzYco+iRuODwrPIiS4dA+cuHRjXPByI9pepWqsubRoLAVV5Sj6tESnPHI3JgLj/mwfX3RndESLGy/3DV079mbUY/zWHj7WN7ZfCvy7w+ykiMayKR82bKfIehzLB1TtrMuLjHuxKSBiqofP352BHkYJ+bQE6jt9vOTXnBAjR3tHcAzBtbKDws8KnkaSqC2bDgrXWDwfwdSigVxIC34wJz44d9vpwJopx4/3RWd6ekZHePevfXPh8w/thJUnUahR5YuyLxvGi1F9M2GqRlF4YsChnTywAE5N22CNVmA2o0Hf9zcHQhZ5yDyL9r4vHTpvUUx0auyAcLKbtm+vetYZvU3Bl07yMrRmv+p7nHOAd+9bczHHoh+fl0maaep1I15hCd6c9EUzWYvhgQg1yBe0M1Q5bpUJ++MO2Vba2V2jGklrO+sAx/hmEof3c9a/6sGDuz8phf5sLKJ69bDGgEZ8FwXiZMi6lXJCgv5lSt7KuoqMwW+YVYjGWAcMDvH0Jo10M4blguhmyrH+i2MrSTzLEhFSiakeFKKGVsa5RKkTPmS/4n9WcK9QMt1DfpLWZYMYeHNQ47YoqyF1dVH3JOfs5Kj6sqXE4J0M2eVsnEeR8cqf5J171WBn1bW1Yt7lbLqFezsZx/OS+39xC3K6nKRzhwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;featured image - riding-backwards-on-rope&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/6aa3764e7fea02f8c9bca20c5ff69566/01e7c/riding-backwards-on-rope.png&quot;
        srcset=&quot;/static/6aa3764e7fea02f8c9bca20c5ff69566/ff46a/riding-backwards-on-rope.png 325w,
/static/6aa3764e7fea02f8c9bca20c5ff69566/01e7c/riding-backwards-on-rope.png 512w&quot;
        sizes=&quot;(max-width: 512px) 100vw, 512px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Dev/prod parity is one of the &lt;a href=&quot;https://12factor.net/&quot;&gt;twelve-factor app&lt;/a&gt; principles. It clearly encourages us to “Keep development, staging, and production as similar as possible”. It is a very important rule and we try to follow it in most of the projects we work on. However, as for every rule, we can see exceptions (that of course are followed by tradeoffs).&lt;/p&gt;
&lt;p&gt;In the past we used Vagrant to create development environments using Chef cookbooks to provision them with the same software as production. Nowaday, we use Kubernetes to create development environments and Helm charts to provision them with the same software as production (using the same Docker images).
From a developer&apos;s perspective these approaches have disadvantages. They are slow to start, developer’s experience is mediocre at best (compared e.g. to live-reload some frameworks provide), and troubleshooting requires understanding the technologies that are running them (e.g. Chef or Kubernetes). What is often worse, automation often does not help these problems. It is very hard to learn higher-level automation frameworks without understanding the basics. Writing Chef cookbooks without basic knowledge of Linux and Ruby or creating Helm charts without understanding Docker and Kubernetes is like trying to learn to ride a bike for the first time by riding backwards on a rope over a cliff.&lt;/p&gt;
&lt;p&gt;For product development teams this steep learning curve is acceptable as they will use the tools on a daily basis (even if they need to learn them) and they will benefit greatly from using them. However, for the “scouts” like architects, who want to evaluate the software without investing much time in running and maintaining the environment, running a full-blown environment might be a big obstacle. They want to try the software as soon as possible, without spending time on learning how to use all the shiny tools the development team uses.&lt;/p&gt;
&lt;p&gt;This is where we asked ourselves: is dev/prod parity the best solution for demo environments?&lt;/p&gt;
&lt;p&gt;What alternatives do we have? The answer to this question is not as straightforward as we would like it to be, so let’s work on an example -  have a stateless web application that keeps its state in a database. Normally, (especially if this application is part of a more complex platform) we would containerize it and run with Kubernetes Deployment together with database Stateful Set. But modern cloud offers much simpler methods of running containerized applications without the need to learn Kubernetes, Chef or any Infrastructure as Code frameworks like Terraform. Services such as Google Cloud Run or AWS Beanstalk enable running containers with just a few clicks in their web consoles or single CLI command in serverless fashion.&lt;/p&gt;
&lt;p&gt;Of course, we are left with the issue of the database, which needs persistence (which, in turn, is not a native functionality of serverless platforms). But also in this case we can rely on cloud services e.g., we can configure managed database service and use it with our container running in GCP Cloud Run (such setup is usually equally simple as using aforementioned container execution services).. In some cases, we can go a step further and completely abandon the external database (e.g. replacing it with in-memory database or local container filesystem).&lt;/p&gt;
&lt;p&gt;As we mentioned at the beginning, this setup is not free of tradeoffs. Here are some of them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;no dev/prod parity - the production environment is not the same as the demonstration environment and sometimes requires small hacks and/or workarounds (e.g. in-memory database),&lt;/li&gt;
&lt;li&gt;performance - the web application container resources are limited and the performance can be worse than on the production environment with e.g Kubernetes,&lt;/li&gt;
&lt;li&gt;the data is not persistent - the web application container is serverless, so the data stored in the container is not persistent,&lt;/li&gt;
&lt;li&gt;reliability - the number of application replicas is limited to single instance by lack of database sharing,&lt;/li&gt;
&lt;li&gt;costs - running workloads in serverless environments such as Google Cloud Run or AWS Beanstalk is more expensive than running the same workload using similar resources in Kubernetes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see, the list of tradeoffs is not a short one. This is the cost of making things simpler and reducing cognitive load required to attend potential partners and customers for more advanced products. Enabling engineering teams with various skill sets to explore or do a quick proof of concept is a key factor, which makes these tradeoffs acceptable in a given context.&lt;/p&gt;
&lt;p&gt;This concludes our demo/prod environments parity discussion. The twelve-factor app principles are the foundation for building web applications. However, they are not the golden rules, and as always, everything depends on the context.&lt;/p&gt;
&lt;p&gt;Check &lt;a href=&quot;https://www.websight.io/&quot;&gt;websight.io&lt;/a&gt; blogpost &lt;a href=&quot;https://www.websight.io/blog/2023/gcp-cloud-run.html&quot;&gt;“Introducing a streamlined approach to showcase WebSight CMS using GCP Cloud Run!“&lt;/a&gt; to see a practical example of demo environments simplification.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Why content-centric architectures depend on caching]]></title><description><![CDATA[During my last presentation at AdaptTo() 2023, I mentioned reference content-centric architectures and how they leverage caching. In this…]]></description><link>https://handsonarchitects.com/blog/2023/why-content-centric-architectures-depend-on-caching/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2023/why-content-centric-architectures-depend-on-caching/</guid><pubDate>Tue, 17 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/13b1ed660bbc2c720a17444a0cb7b6d8/e5715/computer-cache.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEz0lEQVR42gHEBDv7ACRMuxZGvRNFvChOrUNdqRpErk9zuHOLtjBXqUtvum+HwS5VshlKvCdSwSVOwSZMwCRQwyVPwSNLvCBNvQAEQLs4Y7mBn9PX3OfZ2tOWoKOhooTOxYqyr4TP0J/n4snQ09dBZ7YANMYCOccAQdIDSdQBRM8ARM8CQ8sANWar+fXo+vv1sa6RqKiXvb25s6xctbBQfIiVl6CWtLusmZyXkJqng5nBp7bSPGm+AETYBVDcDFLWC1XWAMTN1fb8/7/GvqConLrO5pqdmKOjTLrBb5+qxKawvL3P56u9za+wpuv09Pv/+tbc3V+CtydhxwBT3xBf3QDn8fbX6Pm8xsagqaO2x+W4voyfoDW7wHWCjqd6ipitvs+4wsmrsKbO3u/V5PLe6vT8+/G4wcgra8ETauQAzNzwzeL8tL+8q7GuobLTjJpxq6kmpq5ejJ3BkqfAmarBhpGgpa+twdbuzd/x0ePz1eX06/X75uTeOn/OAM/b7sfg/629vISGdnJ3a3ZzOn12IZmUR31+ZYaEYY2QdYiIVpighanE4sPY7cne9NTl9bnGzomowTWJ3QCbsseOnIqDimmUk3KYk2aLjGmHjXp+e2B5dTuMj2yXo4WwtKFkkLI6hrmZs8zC0+fo7O+Ap8Yjj+46m/UAi6zBi45qf4ePzNrRtbmSjpeirLm5vM7dmJZbn6aIub66sr3Fm7vQn7zKbZq4mbfRg7HPQaTrS6z6Sar4ANbg66SkmoSLnL/Fraqtf5GftomVn66/06ShUpahh5WeqbnDxf/////6+JK82my031Oz7lK4+1i19la19QDZ4Odnc2BaY1WGh2aLg0x7f3dka2JbX1p4eTeGiVd1goe9y93U1cOcr5xppbZkfpNUm8Nfxf9iw/lcxPoAnLC8P2B6PFNkWGd/T1ltW2NkZXJ7go2WYpCedqW4m6aNva86rJoFpJkKioMucG5lV1s4WJCkbNT+Z9L3AGWtvYCGnZSluYqctoabsoKRos7e89Hf9Gl0ejpJVVddQG5vP2xvRmVmR1dVNl9aMVNLNFSWp3bq/3Tj+gBvt8CGjqTG2PCgr8KDkaV7iqTE1OnI1OhEa4QtUGlLa4lAW31IZ4NPb489SV9HWmo7V2ljq7qB9v+C5/oAbcDFlqK+tcfiorTJh5esd4iiwtLpxc/mQX2bN22OcKaEqaUucn1Lb9LNWoSLUn2BTpCTa6mrjv//jO71AHS8ZJesz6i82bDF4ZOmwXWBl7zN58DL3T9zlSpVjLumFcO1OpaEQYbQQ2KZZVh2VGGMXG6gWI7vX4zpYgB0vjSMoNClueCnvduWqMRxfZK6zeC7xeZllG1SlDjEsgz/1wCflQl61itll0ZSVl+FhFqDe09jcDx90SsAiOA1d69XfpWlm6ramarUZ3KQtsPll6PIfr1Pj/YzgMcxh7UlbcIwjvU8YIlNTVNncINIcnxeaJZQi+Y5AInjOIvrMnvQM3WqXHaXi1p1cHmqcHnEQ4bgOYzmPYvrP4fpP5HwO37SLFWCQlmMUIjuMoLcOIvpPIzoPgCG4zqI5ECM6T6K6zh/3DR81zKG5zSK6TyM50CL5ECJ5T2K5DyK4jyM5UCH4DuD4TqL5T+M5T+I5ECJ5T+SVr3XVAlfTwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;featured image - computer cache&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/13b1ed660bbc2c720a17444a0cb7b6d8/e5715/computer-cache.png&quot;
        srcset=&quot;/static/13b1ed660bbc2c720a17444a0cb7b6d8/ff46a/computer-cache.png 325w,
/static/13b1ed660bbc2c720a17444a0cb7b6d8/a6d36/computer-cache.png 650w,
/static/13b1ed660bbc2c720a17444a0cb7b6d8/e5715/computer-cache.png 768w&quot;
        sizes=&quot;(max-width: 768px) 100vw, 768px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;During my last &lt;a href=&quot;https://handsonarchitects.com/blog/2023/how-to-sleep-well-at-night-when-your-production-cms-is-down/#recording&quot;&gt;presentation at AdaptTo() 2023&lt;/a&gt;, I mentioned reference content-centric architectures and how they leverage caching. In this blog post, I will expand on this topic.&lt;/p&gt;
&lt;h2 id=&quot;content-centric-architectures&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#content-centric-architectures&quot; aria-label=&quot;content centric architectures permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Content-centric architectures&lt;/h2&gt;
&lt;p&gt;Content-centric architectures are a common approach to building web applications. The content is the most important part of the application, and a central repository stores it.&lt;/p&gt;
&lt;p&gt;An example of a content-centric architecture is a platform built around the Content Management System (CMS). In such a case, the CMS is the single source of truth of all integrations work through it.&lt;/p&gt;
&lt;p&gt;While this approach has advantages, it also has some drawbacks. In this blog, we will focus on one of them - dealing with a high volume of changing data like product information.&lt;/p&gt;
&lt;h2 id=&quot;content-centric-caching&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#content-centric-caching&quot; aria-label=&quot;content centric caching permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Content-centric caching&lt;/h2&gt;
&lt;p&gt;In the case of content-centric applications, caching usually happens between the CMS and the end user, and it is usually a read-through cache with a time-to-live (TTL) invalidation strategy.&lt;/p&gt;
&lt;p&gt;For example, in the Adobe Experience Manager (AEM), the caching happens on the Dispatcher level (Apache Server module). The Dispatcher proxies the request to AEM, caches the responses, and serves them to the end users.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/8b2924d271c64bbe19994aa94ebdefc4/232d9/aem-caching.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 29.230769230769234%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAmUlEQVR42oVQWw7DIAzr/a/AGXonPhHqD49CEkg7j21dVXWaJSJhbMdi2gZUN2bR3ltrIqKq+x2G8vs09QFQLJKpraXknJZlQcqhxrzNmrBHmDNJYCVpRJRSCiEwyAEwpRRcoXbOzfN8NReWQB3mWuvFvA4gAmprrTHmKPKujXY4sTaoY4ze+9eqP7U/H6ZjPoNQBXP/gXPQA/CKXvb3yNBxAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;AEM caching&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/8b2924d271c64bbe19994aa94ebdefc4/3c492/aem-caching.png&quot;
        srcset=&quot;/static/8b2924d271c64bbe19994aa94ebdefc4/ff46a/aem-caching.png 325w,
/static/8b2924d271c64bbe19994aa94ebdefc4/a6d36/aem-caching.png 650w,
/static/8b2924d271c64bbe19994aa94ebdefc4/3c492/aem-caching.png 1300w,
/static/8b2924d271c64bbe19994aa94ebdefc4/d544a/aem-caching.png 1950w,
/static/8b2924d271c64bbe19994aa94ebdefc4/d2433/aem-caching.png 2600w,
/static/8b2924d271c64bbe19994aa94ebdefc4/232d9/aem-caching.png 3705w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/103a3923277ab39ed138aeffc03fc531/232d9/aem-caching-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 29.230769230769234%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAA8ElEQVR42mOor69nAuHQ0FXM8+fv5/i/ahXzmZlnWLdt28a+CshmIAD+///PhCKwf/9+FhAGaQYZ4rfhOu+GI0d4d+/ezb9582ZpoDgbVCMjSA3Icrw2zJ8/n+MW1CClme/4C1cd4wRawAEycNOmTSIg+fXr1wtA+VxAOR6QPMjgly9fSnz69CkYq4ExOy9w68+/L1C//z7HsWNgQwWWLl0KNxDoej6QYUeArgcZDNTK9P79e4UPHz6kobgayAF7+T8oHFf9Z/ZY/IYP5BqQgUCNciADYV4GhRdBL8Mipb7+PxNYEzCcQBaAwhNEM5AIAE8goLWWb1eiAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;AEM caching&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/103a3923277ab39ed138aeffc03fc531/3c492/aem-caching-dark.png&quot;
        srcset=&quot;/static/103a3923277ab39ed138aeffc03fc531/ff46a/aem-caching-dark.png 325w,
/static/103a3923277ab39ed138aeffc03fc531/a6d36/aem-caching-dark.png 650w,
/static/103a3923277ab39ed138aeffc03fc531/3c492/aem-caching-dark.png 1300w,
/static/103a3923277ab39ed138aeffc03fc531/d544a/aem-caching-dark.png 1950w,
/static/103a3923277ab39ed138aeffc03fc531/d2433/aem-caching-dark.png 2600w,
/static/103a3923277ab39ed138aeffc03fc531/232d9/aem-caching-dark.png 3705w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Invalidation rules for Dispatcher caching can be challenging. They depend on complex content relations such as PDP pages, listing pages, and &lt;code class=&quot;language-text&quot;&gt;.stat&lt;/code&gt; files. For more information, you can refer to the &lt;a href=&quot;https://experienceleague.adobe.com/docs/experience-manager-dispatcher/using/configuring/dispatcher-configuration.html?lang=en&quot;&gt;Dispatcher configuration guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;sample-solutions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sample-solutions&quot; aria-label=&quot;sample solutions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Sample solutions&lt;/h2&gt;
&lt;p&gt;To make things more clear, let&apos;s work with some example. We will use a simple Product Details Page reference. The solution involves the CMS, which is the source of content and layouts of pages. Product Information Management (PIM) system is the source of product data presented on the product pages.&lt;/p&gt;
&lt;p&gt;Below, you can find three examples of content-centric architectures that enable displaying product data on the Product Details Page.&lt;/p&gt;
&lt;h3 id=&quot;batch-updates&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#batch-updates&quot; aria-label=&quot;batch updates permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Batch updates&lt;/h3&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/1e2033ea12ff4bd6ce1714b8628b3d4f/184ac/batch-updates.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 12%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAIAAADXZGvcAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAYElEQVR42h2L2wlAIQxD3f/LNZzAIZxEEMSW+qh6e4P5COSQ42qtOeepe5+L0muNCERE1lobRJWISimtNX0xs5RSCMHhxEyQdZ+J+/mkD2buvY8x5gsmfMgg4JBjjN77HxOzcApSsVqgAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;batch updates solution&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/1e2033ea12ff4bd6ce1714b8628b3d4f/3c492/batch-updates.png&quot;
        srcset=&quot;/static/1e2033ea12ff4bd6ce1714b8628b3d4f/ff46a/batch-updates.png 325w,
/static/1e2033ea12ff4bd6ce1714b8628b3d4f/a6d36/batch-updates.png 650w,
/static/1e2033ea12ff4bd6ce1714b8628b3d4f/3c492/batch-updates.png 1300w,
/static/1e2033ea12ff4bd6ce1714b8628b3d4f/d544a/batch-updates.png 1950w,
/static/1e2033ea12ff4bd6ce1714b8628b3d4f/d2433/batch-updates.png 2600w,
/static/1e2033ea12ff4bd6ce1714b8628b3d4f/184ac/batch-updates.png 5736w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/a4251bda10fc2c88876dd26eca763e72/184ac/batch-updates-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 12%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAYAAABYBvyLAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAAm0lEQVR42mM4c+aMyNmzZ6Xq5+/nqN//n2XVqlVsDDPPsK46dkzo/Plj0vv37+e5desW+8yZM1mvXLnCdv78eYFLly6Jg+ht27axgzADEHz9+tXk8+fPYQxARTxAQ/nr99/nyJ24jX3VqmOcKhNvsa/aDxTfvZsfZOCmTZu4gBZxgvBuoBhI/eHDhwWBlnCB5EEGfvjwwQNoYD4AExdX0PYrjFUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;batch updates solution&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/a4251bda10fc2c88876dd26eca763e72/3c492/batch-updates-dark.png&quot;
        srcset=&quot;/static/a4251bda10fc2c88876dd26eca763e72/ff46a/batch-updates-dark.png 325w,
/static/a4251bda10fc2c88876dd26eca763e72/a6d36/batch-updates-dark.png 650w,
/static/a4251bda10fc2c88876dd26eca763e72/3c492/batch-updates-dark.png 1300w,
/static/a4251bda10fc2c88876dd26eca763e72/d544a/batch-updates-dark.png 1950w,
/static/a4251bda10fc2c88876dd26eca763e72/d2433/batch-updates-dark.png 2600w,
/static/a4251bda10fc2c88876dd26eca763e72/184ac/batch-updates-dark.png 5736w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Batch updates are one of the most popular PDP solutions. The CMS is the single source of truth responsible for rendering the PDPs. The CMS integrates with PIM via batch updates executed on a regular basis. CMS storage duplicates the data from PIM. That enables PDPs rendering with no additional callbacks to PIM (PIM is not taking part in end users&apos; requests). The trade-off is that the information on PDP pages is outdated. That might or might not be a problem, depending on the requirement on how often PDPs should change.&lt;/p&gt;
&lt;p&gt;This solution works well when the data in PIM changes rarely, and most of the page responses come from the cache. In such cases the batch updates can be scheduled to run once a day, or even less often. Another problem could be the volume of products. When using a cache warm-up, rendering several tens of thousands of PDPs could take up to a couple of hours. The real problem starts when the products need to be updated every time they change in PIM within a short period of time (e.g., 15 minutes) - which was the case in one of our projects in the past.&lt;/p&gt;
&lt;h3 id=&quot;backend-integration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#backend-integration&quot; aria-label=&quot;backend integration permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Backend integration&lt;/h3&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/ec6e73b67011107081ea44338b752247/184ac/backend-integration.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 12%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAIAAADXZGvcAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAWElEQVR42j3LQQoAIQiF4e6/6hqdwEN0EiEQi2q0RT0mZlwIfvgHEWHmMYa7r7WwVRXSezezK7XWUgrc39l755xTSgFPrTXEzze/zDmv4ESPGAJHTEQxxgMCFW/NdJSBDQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;backend integration solution&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/ec6e73b67011107081ea44338b752247/3c492/backend-integration.png&quot;
        srcset=&quot;/static/ec6e73b67011107081ea44338b752247/ff46a/backend-integration.png 325w,
/static/ec6e73b67011107081ea44338b752247/a6d36/backend-integration.png 650w,
/static/ec6e73b67011107081ea44338b752247/3c492/backend-integration.png 1300w,
/static/ec6e73b67011107081ea44338b752247/d544a/backend-integration.png 1950w,
/static/ec6e73b67011107081ea44338b752247/d2433/backend-integration.png 2600w,
/static/ec6e73b67011107081ea44338b752247/184ac/backend-integration.png 5736w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/ae24d06121322f4cb56455243be8f6e3/184ac/backend-integration-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 12%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAYAAABYBvyLAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAAiUlEQVR42mM4c+aMyLVr1yQvXLjAfeXKFbZVq1axgehLly4Jnj9/Xnr//v08t27dYp85cyYrSBwoJgCUEwfR27ZtYwdhBiD4+vWryefPn8MYgIp4Tpw4wQfSCDSME6iRa9OmTVwg/u7du/lBNIgPkgNhkBjQEfyHDx8WBKkFyYMM/PDhgwfQwHwAh0Far7Q+9K8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;backend integration solution&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/ae24d06121322f4cb56455243be8f6e3/3c492/backend-integration-dark.png&quot;
        srcset=&quot;/static/ae24d06121322f4cb56455243be8f6e3/ff46a/backend-integration-dark.png 325w,
/static/ae24d06121322f4cb56455243be8f6e3/a6d36/backend-integration-dark.png 650w,
/static/ae24d06121322f4cb56455243be8f6e3/3c492/backend-integration-dark.png 1300w,
/static/ae24d06121322f4cb56455243be8f6e3/d544a/backend-integration-dark.png 1950w,
/static/ae24d06121322f4cb56455243be8f6e3/d2433/backend-integration-dark.png 2600w,
/static/ae24d06121322f4cb56455243be8f6e3/184ac/backend-integration-dark.png 5736w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The concept behind this approach is straightforward: the CMS takes on the role of rendering PDPs. In this configuration, the CMS is directly linked with the Product Information Management (PIM) system, pulling data from the PIM at regular intervals, typically triggered by user requests reaching the PIM.&lt;/p&gt;
&lt;p&gt;Directing user traffic to the PIM usually requires an additional caching layer (between CMS and PIM), introducing complexity into the platform. Furthermore, the activities of the PIM and CMS teams must be synchronized. For example, PIM updates may temporarily prevent CMS authors from publishing PDP pages because it will clear the cache.
Techniques like cache warm-up and advanced cache invalidation are often employed in this context.&lt;/p&gt;
&lt;p&gt;This method is most effective when dealing with a modest volume of products and when the PIM can effectively handle the incoming traffic.&lt;/p&gt;
&lt;h3 id=&quot;frontend-integration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#frontend-integration&quot; aria-label=&quot;frontend integration permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Frontend integration&lt;/h3&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/db135ac22ed4d2b926106b5ae8f53dfd/f6350/frontend-integration.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 25.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAmklEQVR42mVQWwrEIBDz2N7Ji3gBD1CkBQWr1EdlfeyGHVp2aT5iHGYSR/b+x5yzlHIcR621tdZ7B+ecY4xguqJtWRYpJaOB3/kQgtaaus/zhJf3fl3Xfd+hUUePEIJzzuA0xoDlnYwr8V15XbgDYGetZTiMMQgpD+QvUkrYAj1gaKpDwII557Ztw4aU/wTqeLNSClHQ9BG06QcGCB8LnjfgPgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;frontend integration solution&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/db135ac22ed4d2b926106b5ae8f53dfd/3c492/frontend-integration.png&quot;
        srcset=&quot;/static/db135ac22ed4d2b926106b5ae8f53dfd/ff46a/frontend-integration.png 325w,
/static/db135ac22ed4d2b926106b5ae8f53dfd/a6d36/frontend-integration.png 650w,
/static/db135ac22ed4d2b926106b5ae8f53dfd/3c492/frontend-integration.png 1300w,
/static/db135ac22ed4d2b926106b5ae8f53dfd/d544a/frontend-integration.png 1950w,
/static/db135ac22ed4d2b926106b5ae8f53dfd/d2433/frontend-integration.png 2600w,
/static/db135ac22ed4d2b926106b5ae8f53dfd/f6350/frontend-integration.png 3989w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/32f33b09c8cdbc3b854f1f1c593606d1/f6350/frontend-integration-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 25.23076923076923%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAA7UlEQVR42nVQSwqCUBR9/ajAyuxDRFG0hQatpPU4bgmC4ECcOHIkggMn/lEnugIXILoCuycSHNSFy33388459zL2w9q2HRiGsXAch/d9fy5J0kQUxTEi1bgkSVau66I/pvcEf6qqujRN82D/LIqiTRzHN9u2V6ZpLmVZXoCEALZpml48z9sj1zRtjfm6rp8E+GJgALuu66O+QuRd7NcJfKooygyxL6AsS6EoisNHCTEeoQLrdE5AnKqqS0SsBw/DEDM8+cmyLOGbc0TAd0pZlmW7PM/PuBXU4C7/HP0gCK4k4E4ER1I87Hq05RCAb+ibtKWYI1h5AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;frontend integration solution&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/32f33b09c8cdbc3b854f1f1c593606d1/3c492/frontend-integration-dark.png&quot;
        srcset=&quot;/static/32f33b09c8cdbc3b854f1f1c593606d1/ff46a/frontend-integration-dark.png 325w,
/static/32f33b09c8cdbc3b854f1f1c593606d1/a6d36/frontend-integration-dark.png 650w,
/static/32f33b09c8cdbc3b854f1f1c593606d1/3c492/frontend-integration-dark.png 1300w,
/static/32f33b09c8cdbc3b854f1f1c593606d1/d544a/frontend-integration-dark.png 1950w,
/static/32f33b09c8cdbc3b854f1f1c593606d1/d2433/frontend-integration-dark.png 2600w,
/static/32f33b09c8cdbc3b854f1f1c593606d1/f6350/frontend-integration-dark.png 3989w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;While CMS still maintains a central role, its responsibility for rendering  PDPs has shifted. Instead of PDP rendering, the CMS provides templates (e.g., with ReactJS components), and the actual assembly of pages occurs within the user&apos;s browser. This transition results in the PIM system exposition to direct user traffic, which necessitates the implementation of additional caching mechanisms, typically achieved through an API Gateway.&lt;/p&gt;
&lt;p&gt;In short words, a significant portion of product-related caching has been transferred to the API Gateway, making it a crucial part of the caching strategy.&lt;/p&gt;
&lt;p&gt;It&apos;s important to note that assembling pages on the front end introduces trade-offs in User Experience (UX), especially when dealing with a substantial volume of products.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;We have seen three different approaches to producing product detail pages with data from two different sources: CMS and PIM. Each of the solutions depends on caching.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In the context of caching, batch updates are the simplest to implement, but they might contain outdated data.&lt;/li&gt;
&lt;li&gt;The backend integration serves the latest data but requires additional caching and synchronization between PIM and CMS.&lt;/li&gt;
&lt;li&gt;Finally, the frontend integration with fresh data comes at the cost of an additional caching mechanism for PIM (e.g., API Gateway) and trade-offs on the UX (especially when we talk about a high volume of products).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;resources&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#resources&quot; aria-label=&quot;resources permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Resources&lt;/h2&gt;
&lt;p&gt;We highly recommend &lt;code class=&quot;language-text&quot;&gt;D4Debugging&lt;/code&gt; blogpost &lt;a href=&quot;https://dfordebugging.wordpress.com/2022/08/17/database-caching-strategies/&quot;&gt;Application Caching Strategies&lt;/a&gt;. It is a great read on caching strategies that nicely summarizes the topic.&lt;/p&gt;
&lt;p&gt;For the presentation from AdaptTo() 2023, &lt;a href=&quot;https://youtu.be/uqwmu_kMHcQ?t=404&quot;&gt;the part about referential solutions starts around ~6:40 minute&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Just-in-time knowlege sharing]]></title><description><![CDATA[In one of the previous blogs, we discussed the idea of a synchronous and non-blocking way for a code review. We pointed out knowledge…]]></description><link>https://handsonarchitects.com/blog/2023/just-in-time-knowledge-sharing/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2023/just-in-time-knowledge-sharing/</guid><pubDate>Tue, 03 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/307b572d4b1dc9c150ac7b65e436dfc6/e5715/just-in-time.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFf0lEQVR42iWVeVDUdRjG10bLmsoOp2M6tHLUbMwMTTFQMBFFQAktlS11LBECUeNQFAE5PAgUFyRAScVAroVlF4R1l133AORYCF2oSESOBQRPNPNoPn3JP975/feZ5/k97/N+JXJ9D4W6bgo1lymqakeh76TceBX9hU7KdH+iVLeh1VsxGlswGC6hVl9CdbaVAmUzCl0npQYbxfpe5DobcvGVFOttnKlsZ/XqLXg5foeHnRSX2atxc5LibL8KhzkeYtxwmL0Su9lL+dRlMdMXLmTWHFeyco1oG28KYLeA2SjSCWCJsZ88dTtuyzbxTogLz+fM46UTjrz682LGGJ0YbXVC0rGIqbIY3iz0R/LIAck1R6Z7OyHPN2Oy3hUqeygacVrVjaSgqgfzxUGC/KN4tuILJHjwXN0ixp515uk2H0Z3r0JyyYFR3UuQDH3GqCF7JP9KeVfqSVluFS1XH6Ay9lJi6H2isEDbRdPlm0QGxfNi5gjQlbEN85iYtpbXo1fwvMabCUlSZqqSWGA4hpPWhEtRBg4edsjzKhm8CyoBUxpHLNuQFAmZ9e032eN3gJcz3Rhzazle5WVsUdtYmnqa6akhBFUP4Ft6mfBmSKnMpLPiLTpyJFSXr6L9rz609deF7V6KzwugXNdDW8899oYkMyZ5PrNNRwnMH2BtaSMfZexiQUIWQZZhfI09eOfUExvsTkP8VGwJM6k79gZNjW3UWIcpNwnbIhwB7OYP2wOiwhJ5IXgxPuctzDmwjxVnTLhnG5gkD8G33oav5R8Olxqou9BBhJ+MrIhY8o8eZPg+mJuHqKzpR3F+ZG0EsKXrIdHBCYzzdcXP0ss35bWsUzQT3gRzSo6wvqwaf1UvAactlOZuRZ2+hCs53uzbtZkbdx5h+f02Z6v7/rctFHbR2veYxB0yntszj5lNBYR3QEwnbGu8zSRzKBOVW/mwIoKJBX7MiHFl7UZHGvNnEBu4lnTxj6/Y7nG++cYT4IjCVttjDuw8zNg1M3gmw54p6wKwi0rk/RBPZu1eg91GTxyk7ki/X8+23SdY5/k1HvEnWRmQQXKohMqKUGp/u0eZWYSiFDtkvDiMLL2IsK2JpMkUpCSnEpUUSWXpGX4rrmD35hDiAveQmpTBrTvpmApfwT69Epftv7LR9QOy08ZTXN6CpuG6AJr6qKjtw2i9Q3LETvKljpzcsJx0L0fiNq1mfcAWFnn5IJ35NoX7RqNJGsWhJU/xaZqeFTuO89Xcuch/eQ/jhT+prB1EUmruR1VjE50cIDwsEtkmD7Lnj+fn6S+z9ZPxHFk2GT/7qSxaMJ+wyc+ya9pbhH/4DlOPm/l8u4pvFsxCVzINbc1VyqqvjSjsR91wA61liJjon0T8XQRJpUS6u5Hi54csbCepP6XhvzGQb72+Rp6VRv7haHwiT7E5xA9jloS44LkikH7RFrGHKkFNOW0gNrmQ9d/+gO6CjR8Co5BrWqkXaWtahhGXjKhYGWuk62jKSeLKsQiq9vpzNOQjfFynYG/njsLQLWZgpMudeDh/iaudG1PGTWBPpIwdEQnkKuuobb2DvtEmqnmf0O2RuC39Eo2+lfht4XznvQrnpZvY4LODLdv2ki3XUV4rFBZqr7LceTmfT5jGx6+9z/bg/cQfykZ+zorZehtdUz+Nlx8SGhxLfMIJ2gYgO1/Prs27OXgwk2O/KIiIy+OcwhPrHyniHhr6yNN0cLLsIscVDShN3WgsN8QMoW0aRNPYj/n3v4mJO0qOohqdZYAfA3ayYdlXREWncuhILpH7c1CURFHXVCSaon9yJRSmQZQ1gyjMfSirbZytG6CiXkzDNdSWQVTmTtT1vahMHaRkKsg8dY68CitnylvF09EpnpAuwbnGf3IUTYPPxnfgAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;featured image - just-in-time&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/307b572d4b1dc9c150ac7b65e436dfc6/e5715/just-in-time.png&quot;
        srcset=&quot;/static/307b572d4b1dc9c150ac7b65e436dfc6/ff46a/just-in-time.png 325w,
/static/307b572d4b1dc9c150ac7b65e436dfc6/a6d36/just-in-time.png 650w,
/static/307b572d4b1dc9c150ac7b65e436dfc6/e5715/just-in-time.png 768w&quot;
        sizes=&quot;(max-width: 768px) 100vw, 768px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In one of the previous blogs, we discussed the idea of a &lt;a href=&quot;https://handsonarchitects.com/blog/2023/code-quality-dimensions/&quot;&gt;synchronous and non-blocking way for a code review&lt;/a&gt;. We pointed out knowledge sharing as an example of such a review model.&lt;/p&gt;
&lt;p&gt;Now, we would like to elaborate on that one a little more.&lt;/p&gt;
&lt;h2 id=&quot;how&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how&quot; aria-label=&quot;how permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How&lt;/h2&gt;
&lt;p&gt;In short words, Just-in-time (JIT) sessions are similar to meeting with your colleagues in front of a laptop and trying to introduce them to the work you have been working on lately.
JIT sessions we do are almost always virtual. We started JIT sessions during the pandemic, and since then, the format has not changed thanks to its simplicity.
The single session is up to 30 minutes long. The presenter is in control of who the session is for, what knowledge the attendees should have to gain the most out of the session, and the form (code sharing, brainstorming, Q&amp;#x26;A, whiteboard drawings, etc.).&lt;/p&gt;
&lt;h2 id=&quot;why&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why&quot; aria-label=&quot;why permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why&lt;/h2&gt;
&lt;p&gt;When we kicked off the JITs for the first time, our motivation was very straightforward - we wanted to share our small findings that we usually talked about in the kitchen or hallway and get some feedback. We were looking for an informal meeting that does not require spending much time on preparations (in opposition to other virtual formats we had established at that moment).&lt;/p&gt;
&lt;p&gt;In time, we found many more benefits and explored more applications of this format:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Short sessions are an alternative to more formal knowledge-sharing sessions.&lt;/li&gt;
&lt;li&gt;The simple form encourages more developers to share and tell what things they do on a daily basis.&lt;/li&gt;
&lt;li&gt;JIT meetings have proven to be helpful tool for gathering feedback early on your ideas.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, we would like to share a couple of recommendations for a quick start for the company and for presenters:&lt;/p&gt;
&lt;h2 id=&quot;quick-start---presenters&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#quick-start---presenters&quot; aria-label=&quot;quick start   presenters permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Quick-start - presenters&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Time is of the essence. Don&apos;t wait for a special moment. Just give it a try. You don&apos;t need to prepare much. Talk about what you are currently doing in your project.
&lt;ul&gt;
&lt;li&gt;Depending on the talk type, we prepare between 5-60 min. If this is a live coding session, 5 minutes is enough to have an agenda for things you want to show (it is usually good to have a note on the side to keep in timing). With ideas or summaries, when some graphics are required, we spend a bit more time (but never more than 1h).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Start small, e.g., just in your team. When you are ready, try your department or company meetings. A good time would be a 15-20 min slot right after your daily. Demo a solution you were working at to your team.&lt;/li&gt;
&lt;li&gt;Make it natural and bring the tools you are used to work with. Do not spend much time on preparations. Treat this meeting as you would if you were to introduce your colleague to the project.&lt;/li&gt;
&lt;li&gt;Don&apos;t bring PowerPoint presentations (we even forbid it at the beginning to distinguish this session from other more formal meetings).&lt;/li&gt;
&lt;li&gt;Instead, prepare your IDE (make sure font will be visible to others), notes (e.g., in Markdown format), or some whiteboard tool (Miro works quite well).&lt;/li&gt;
&lt;li&gt;Make it clear what knowledge is needed to benefit from the meeting and to whom you are addressing your speech. You may use hashtags like #docker-basics, #operational-bash, or #advanced-ReactJS.&lt;/li&gt;
&lt;li&gt;Remember that as the presenter, you also want to benefit from the meeting. You share your knowledge, but in return, you want to get feedback and questions that will help you improve your idea.&lt;/li&gt;
&lt;li&gt;Try to leave some space for discussion. We usually try to fit in 15 min to leave the place for conversation and questions (which sometimes leads to 1h+ meetings and vivid discussions, which help to build even better solutions).&lt;/li&gt;
&lt;li&gt;Coupe of examples for a JIT:
&lt;ul&gt;
&lt;li&gt;Just before starting some small project (e.g., tool/library that will help your project to do &lt;code class=&quot;language-text&quot;&gt;XYZ&lt;/code&gt;). Gather your idea, prepare a few points on what you want to achieve and what problems you want to solve.&lt;/li&gt;
&lt;li&gt;When you researched a new technology/library/framework. Share your insights and findings. Give the context of your project/problem you are looking for a solution for.&lt;/li&gt;
&lt;li&gt;After something went wrong or unexpected. Try to prepare some conclusions.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;quick-start---company&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#quick-start---company&quot; aria-label=&quot;quick start   company permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Quick-start - company&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Start with a constant time window (e.g., every Tuesday at 14:00).&lt;/li&gt;
&lt;li&gt;Do not let queue talks. Some simple (but not too many!) rules, like no slot reservation, are okay.&lt;/li&gt;
&lt;li&gt;Create a dedicated channel (or use an existing one) for announcing the topic the morning or day before the session. The first one wins. If you find there are more people who want to present, create more slots.&lt;/li&gt;
&lt;li&gt;Generally, one slot = one talk. Do not stack. Many JITs end up with a discussion (the prize for the presenter - they get questions and feedback there).&lt;/li&gt;
&lt;li&gt;If you have other, more formal knowledge-sharing sessions, do not let PowerPoint presentations. Simple drawings, an IDE, a console, and a whiteboard are all what presenters need.&lt;/li&gt;
&lt;li&gt;Recordings - this is a tricky one. As far as we observed, it is a controversial area. On the one hand, recording allows people who were unable to attend the meeting to watch the recording at any time and ask questions. On the other hand, we have observed that the recording function prevents many people from asking questions and participating in the discussion.&lt;/li&gt;
&lt;li&gt;Generally, we do not recommend imposing the form of the meeting. Let the presenters decide which format they feel most comfortable with. A good balance could be recording sessions and taking questions/feedback after recording has stopped. Another way is to leave notes and key takeaways in some common source like Confluence or GitHub repo README.&lt;/li&gt;
&lt;li&gt;Do not kid yourself. This format, like many others, must have several precursors to be successful. Help them organise meetings and encourage people to participate. If it succeeds, it will be like a good YT video - it will start getting thumbs-up on its own.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tldr&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tldr&quot; aria-label=&quot;tldr permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;Just-in-time (JIT) knowledge-sharing sessions are low-effort meetings that are easy to adopt in the team or companywide. The essence is to share your recent (even daily) work or findings with a wider group of engineers. Don&apos;t bring any presentation. Most of the sessions are live screen sharing. There is also an additional prize for the presenters - discussion, feedback, and questions that might use the company&apos;s brainpower to create better solutions.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to sleep well at night when your production CMS is down]]></title><description><![CDATA[The summer break is over, and it's time to get back to regular blogging. We start this season with a conference appearance. Next week…]]></description><link>https://handsonarchitects.com/blog/2023/how-to-sleep-well-at-night-when-your-production-cms-is-down/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2023/how-to-sleep-well-at-night-when-your-production-cms-is-down/</guid><pubDate>Tue, 19 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1200px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/a8dcbebcdef41026fb59e55568f60a50/c1b63/this-is-fine.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.30769230769231%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsTAAALEwEAmpwYAAACiElEQVR42mNQUVE3MZQ3UhfSUxHWVxPWVhEyVBcw0hDWVRbVURLRVRY2VBPUURYxUBMEMZQETTSFDfQUVVTV1dTUGBQVNRwdZEoy2aO8+dxNhXys+PytBUKd+TOiuVLCucI9+QMcBN3MOMKcOUOd+ENc+bJiuayt5JSU1NXVYZpnTGWYMZFxch17ZxNzaSFHaSFXVhb/jGmMHc0safGS3a1WMyZLT2hnnDGBed5URhsbWSUlDYTmKf0MU3oZTy9nmtLLNL2XcU4by9Ro7hmdrE31DEsWdp+78OTAoQOLpwgeXsSwbCqTlbUcQrOTo8zkCQwze1hPL2Ge2sV0ZAnDii72dWU2sysciguZFswNPbqtYt4s31sbWV9uY5jax2hrg6TZ0VF6Sg/T3jksjzYxTelkfriFoS1BfG6WydFy8cZY/sUTmS+vZD+8kPHbQYbfRxhWTGcE2qwM0Qw0w9pOZsNspidbmK6uYpo7genmGoZDARx3ShnPVDLPn8ZwfbnglUXGH9fofFqp+naD4O4lDGYWcsrKYM3KympWZioH57L9P8PwZCvDgjbeA12qe0Jl1/gILgkXXVjPd3GhwIlelXNT5S9MVr60hG/bTA4zI1VlFTWws1VUXU31ri4S/n+S4fthhiOT5LZV2h0ptZ8Tpj/JS+NErcOJZssT06X3TxK7OE/k/haGI116HmaGCsoqIM1Aysvc4OIcmf8XGF5sY/ywn/HRZs6TrQa7Cx1OllrtKXM+0q/y5QjDzdXcP44w/zrJcKbLzMNcD6pZHqjZQm9rteGpaRJ7eyR3dUof6dDaUW6zo8RxR5nj8lzbUzPEHiwXuLVYEEhemS+6s9LGxVRHUVlVXV0NAIq755b0RoDwAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;featured image - this is fine meme&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/a8dcbebcdef41026fb59e55568f60a50/c1b63/this-is-fine.png&quot;
        srcset=&quot;/static/a8dcbebcdef41026fb59e55568f60a50/ff46a/this-is-fine.png 325w,
/static/a8dcbebcdef41026fb59e55568f60a50/a6d36/this-is-fine.png 650w,
/static/a8dcbebcdef41026fb59e55568f60a50/c1b63/this-is-fine.png 1200w&quot;
        sizes=&quot;(max-width: 1200px) 100vw, 1200px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The summer break is over, and it&apos;s time to get back to regular blogging. We start this season with a conference appearance.&lt;/p&gt;
&lt;p&gt;Next week, together with &lt;a href=&quot;https://adapt.to/speakers/marta-cukierman&quot;&gt;Marta Cukierman&lt;/a&gt;, we will be speaking at &lt;a href=&quot;https://adapt.to/2023/schedule/integrate-grow-repeat-how-websight-real-time-dxp-provides-a-way-to-break-out-of-the-content-centric-model&quot;&gt;AdaptTo() 2023&lt;/a&gt; - &quot;Europe&apos;s leading AEM Developer Conference&quot;.&lt;/p&gt;
&lt;p&gt;The title of our presentation: &lt;em&gt;Integrate, Grow, Repeat: How Websight Real-Time DXP Provides a Way to Break Out of the Content-Centric Model&lt;/em&gt; might be a little misleading compared to the title of this post. It is the main reason I have decided to write this short update, and disenchant the mysterious title.&lt;/p&gt;
&lt;p&gt;During the session, we will showcase architectures we used to integrate with other data sources, such as Product Information Management (PIM) with CMS in the center.&lt;/p&gt;
&lt;p&gt;We will also talk about an alternative approach to integrate with other systems - the one that actually lets you sleep well at night when your production CMS is down.&lt;/p&gt;
&lt;p&gt;And the session will not end with just talking.&lt;/p&gt;
&lt;p&gt;Make sure to join us on &lt;strong&gt;Monday, 25th September 2023 at 17:35 CEST&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Maciej&lt;/p&gt;
&lt;h2 id=&quot;recording&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#recording&quot; aria-label=&quot;recording permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Recording&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Edit: You can watch the recording here:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.25%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;iframe src=&quot;https://www.youtube.com/embed/uqwmu_kMHcQ?si=cIg7qICF8zE8c6R8&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen=&quot;&quot; style=&quot; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot;&gt;&lt;/iframe&gt; &lt;/div&gt;</content:encoded></item><item><title><![CDATA[Zero downtime website migration using cert-manager and free certificate]]></title><description><![CDATA[In this brief note, we will share a simple way to migrate a website that uses the Let's Encrypt certificate from one Kubernetes cluster to…]]></description><link>https://handsonarchitects.com/blog/2023/zero-downtime-website-migration-using-cert-manager-and-free-certificate/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2023/zero-downtime-website-migration-using-cert-manager-and-free-certificate/</guid><pubDate>Wed, 05 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/0939c2ab308431177d21ca90423e5358/e5715/cert-manager-renewal.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEz0lEQVR42gHEBDv7AFeq+VSp9lGq+Wem33miy4ylvf//+qixuXOXu6fY/53S/22r7FiAqo655Hup2HWhz2iYyWKWyWel3FSs/QBVqfhPpvRZrv1Zqe95qNSFnbSlu9Rzo9Nhkb1firJWhbJOhrxIf7dVhbljmNBkm9JWgrRnm9RiqOZVq/kAWKv6VKz6WKv1QJDXeLTpfKfOTnmlZ5XJWpzgVZ/lRovMT6LrQJHaZq7vksHvd6DFWHGQl8Ltg8P9Uan5AFaq+12w+3uiwE6Z3F2r9E96pJ7L+qy0nZObfVKd51KOymaWwWmn12Oz+G+q4rDH2dvb2puwx2qp51Gt/wBXrftals1cirJmka5Pl9ldms5omM6WfUOyjEh+pMyCs+CZyO+R0ftgtf9Ef7avu8XQ2+JNeqdRi8VToOAAU5TSlJecj3UvzqQvmZhxYoqrTYzIYqrxX6LhhbnrT4KzOXq9NXS0VaPrVqftPXiqT4CtYpjLeqHDfK7YAEiEv2CWy3qQit+6ZsepbmmBk0ZzoDZad1lfUGplSWxfOaJ2I5BZFQ4yWVqBp3iYuJO32pvE7H+q0XqiygBZr/hVoOJNj8hpfYs/W3gFH0IADzuOYRf9pQymmYG2r6H/tDzWmjkOJ0YIJkkGLFMRO2gjTnZfhqyPvOgAXbH+YaDbDDNZCSlFNENEaWdKFS9AeH6A//Ha4OTm+/z8////0drbFTFIV1czaGlHaFIgQzgkBCVKdJ7JAFGl83C5+hJFdV1IIvemH7Wni0BZdGN4i////////9Ta5Ort8+Tm5xU1UJSPf9bHwvnPh659OBM+a5HE8QBTpO1fsvwqZZlRW2D37d349vN2i55LYnj////N1+JshqXG0+Hs7/AUM1Czv8v///////+bqbIxW4R+rd0AW6z1Uaz/QGmJRVhs/f//0NrlX3WVNU9n+fXt0NXeiZm+2Nvo8PLyHTtZp7TB1dnk8/T6kp+pLWCSUpvgAEaR2YSmk56LPUVRV+ns6c7R32t5lCA+Wd7l5maWti91rEiLtaK9zT5UaoadrmGMvNDg8IuXoD6Avm3A/wBMn+uIkGbMpkxbZmbK1d5sn8Qiap4XOFa9y9Gs0OCmx9e0zde3wsclP1eInKGEvdSoztJgeI9FktxarvwAVaz8Uaf8ZIqlIkpumq25sNLikKy6DS1LOFFpW2x8OU5iGDRLAhs9ABlAMkNTR1hmP1ZqIjtSWJvcVa3+AF6s9l+w+FWn9SJLdCxHYVNmeRItRwgnRA00VR5Gaj1jiE14nlNiZTE5NSlaiyhXgR1BXx5CYVWY2Fev/wBrtflerPJjt/9EeakDGjYbPWNLbIReWTeaxOam1/+o2Pqw6f+GnaVmdXyfvcmOjn5hqu9isfhYqfVUqfcAVabyVKbxVqn2VKXuVIe1fqvSs9/7Woedd6PLq9v/msfwnr/PkYtqfI6ZipSGopFeTZfhWrH/Wav3U6j2AFep91Sq+leq+Veq+WW5/1qo7nGk14y04G+ezGSd1GaOorGOHty5Ur+gQ1yUxEme71Kk71Op+Fes+VSp9QBRqfpSqfpUqfdUq/hPqPhar/1UoeZJktRPoOxms/lSqv15ioPLpVqvoGZQo+pYr/9VqPdRpvRUqvpUqfeZe7ZILJop8AAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;featured image&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/0939c2ab308431177d21ca90423e5358/e5715/cert-manager-renewal.png&quot;
        srcset=&quot;/static/0939c2ab308431177d21ca90423e5358/ff46a/cert-manager-renewal.png 325w,
/static/0939c2ab308431177d21ca90423e5358/a6d36/cert-manager-renewal.png 650w,
/static/0939c2ab308431177d21ca90423e5358/e5715/cert-manager-renewal.png 768w&quot;
        sizes=&quot;(max-width: 768px) 100vw, 768px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In this brief note, we will share a simple way to migrate a website that uses the &lt;a href=&quot;https://letsencrypt.org/&quot;&gt;Let&apos;s Encrypt certificate&lt;/a&gt; from one Kubernetes cluster to another with no downtime.&lt;/p&gt;
&lt;h2 id=&quot;starting-point&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#starting-point&quot; aria-label=&quot;starting point permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Starting point&lt;/h2&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/ca9e85308afdbb1043952ee0218f5c3b/b0096/starting-point.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAvElEQVR42o2RWw7CIBREuzDjNkxcgDtyQa7AmPiv0Q8Rgo/yKHChdSiJMdFY5o8bZs4daIYf6qEY4+2yvLPFe/h9r/l0hBCcc1prY6z3nh3n4jSbMBcOEUkpOedt2xpjMBymlM0ppRKhlAIcKYCPk+F/QgMPjcIBTBpXALkkTpPhYYxljzWoSsF3XYciVeayai6ZeuvS9W61sVXk/FopQ6j3h8dOKC6fHpPatVETHyHseb1d7cUmB0Wqee0X974P/OTquPEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Starting point&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/ca9e85308afdbb1043952ee0218f5c3b/3c492/starting-point.png&quot;
        srcset=&quot;/static/ca9e85308afdbb1043952ee0218f5c3b/ff46a/starting-point.png 325w,
/static/ca9e85308afdbb1043952ee0218f5c3b/a6d36/starting-point.png 650w,
/static/ca9e85308afdbb1043952ee0218f5c3b/3c492/starting-point.png 1300w,
/static/ca9e85308afdbb1043952ee0218f5c3b/d544a/starting-point.png 1950w,
/static/ca9e85308afdbb1043952ee0218f5c3b/d2433/starting-point.png 2600w,
/static/ca9e85308afdbb1043952ee0218f5c3b/b0096/starting-point.png 2928w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/795217c4cd7013063cf2a02c2bc8c33a/b0096/starting-point-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAPoAAAD6AG1e1JrAAABAElEQVR42mNgwAf+/2fcv38/y4/3Mit/fZBcxUAq+A80YNu2bezHjh3jXL9+vcCcORt4gQZy/Hgvf/TnB5ljJBkEcsn8+fM5Fi9eLAOkFebOnSu6atUq/vr6eiZSHcYI1MQCY2/ZskUQZDBQjG3p0qWCDGQARqBL2EAYxAEaIjJz5hnWiRMnsk+bNk2QHBeCDVmwYIEyyJCZq2byAw3n3LZtIvucOXNAYchCsoGgiAC5EOQaUHhaJR3htSlYo9TePk0QKM5McszW74eEYf1/Lbb6U7ZWmWsylZzS90iDxEk2EARmnpnJCqKbzzgqN5y331t7ys4XbMGqejaQhaQYBgBh536HN2obYAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Starting point&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/795217c4cd7013063cf2a02c2bc8c33a/3c492/starting-point-dark.png&quot;
        srcset=&quot;/static/795217c4cd7013063cf2a02c2bc8c33a/ff46a/starting-point-dark.png 325w,
/static/795217c4cd7013063cf2a02c2bc8c33a/a6d36/starting-point-dark.png 650w,
/static/795217c4cd7013063cf2a02c2bc8c33a/3c492/starting-point-dark.png 1300w,
/static/795217c4cd7013063cf2a02c2bc8c33a/d544a/starting-point-dark.png 1950w,
/static/795217c4cd7013063cf2a02c2bc8c33a/d2433/starting-point-dark.png 2600w,
/static/795217c4cd7013063cf2a02c2bc8c33a/b0096/starting-point-dark.png 2928w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;We start with a Kubernetes cluster that contains the current website in the USA region.&lt;/p&gt;
&lt;p&gt;The important thing to notice is the &lt;a href=&quot;https://github.com/cert-manager/cert-manager&quot;&gt;cert-manager&lt;/a&gt; installed via Helm configured on the cluster with the Let&apos;s Encrypt ACME production issuer.&lt;/p&gt;
&lt;p&gt;The issuer is triggered by the ingress-shim annotation &lt;code class=&quot;language-text&quot;&gt;cert-manager.io/issuer&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; networking.k8s.io/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Ingress
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;kubernetes.io/ingress.class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx
    &lt;span class=&quot;token key atrule&quot;&gt;cert-manager.io/issuer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;handsonarchitects-letsencrypt-prod&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;tls&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;hosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; www.handsonarchitects.com
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; handsonarchitects.com
      &lt;span class=&quot;token key atrule&quot;&gt;secretName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prod&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;handsonarchitects&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;tls
  &lt;span class=&quot;token key atrule&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; www.handsonarchitects.com
      &lt;span class=&quot;token comment&quot;&gt;# ....&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; handsonarchitects.com
      &lt;span class=&quot;token comment&quot;&gt;# ....&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As a consequence of &lt;code class=&quot;language-text&quot;&gt;cert-manager&lt;/code&gt; work, the &lt;code class=&quot;language-text&quot;&gt;CertificateRequest&lt;/code&gt; resource is created and finally, the &lt;code class=&quot;language-text&quot;&gt;Certificate&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Secret&lt;/code&gt; are obtained and saved.&lt;/p&gt;
&lt;h2 id=&quot;migration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#migration&quot; aria-label=&quot;migration permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Migration&lt;/h2&gt;
&lt;p&gt;Now, we will deploy the application on the newly created cluster in Europe. All that we need to install on the fresh instance are Nginx Deployment, Service and Ingress.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/aca8a142ac5a691b8960a027a8774ed9/b0096/migration.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAABCElEQVR42oVRS26DMBDlYFWuUakHyDK36aZn6CW6yDpKlKxbwaIuhBAD/tvQZ09EghIpI0D2zPvMDNn4IAaE9/70+9aw1yl5j8tuGdZarXXf90JIYwz7WVT5yxMy+Tjn6rouy7JtWyEEkhMopCBpIHGmakY1kui6DgiowDxl4kNMXFFSShENEjhkeF0KpODpUgtwJgLhkMEVX8wyIxOHMRY5UqDsrIEDytMuMAvn3Foj5UX0SqZWo2QYpA5/jeqFDMHfOvvgp/5nbfuEc4P5Pm+rrjyesRVHi0HEwxA5jWZ5e4DDzBlQ/IhKFu+b5b76Gv2ojQYNu8UUwGmjkFwXnx+7lbAcUjTUP/p2C1ImS+2EAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migration&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/aca8a142ac5a691b8960a027a8774ed9/3c492/migration.png&quot;
        srcset=&quot;/static/aca8a142ac5a691b8960a027a8774ed9/ff46a/migration.png 325w,
/static/aca8a142ac5a691b8960a027a8774ed9/a6d36/migration.png 650w,
/static/aca8a142ac5a691b8960a027a8774ed9/3c492/migration.png 1300w,
/static/aca8a142ac5a691b8960a027a8774ed9/d544a/migration.png 1950w,
/static/aca8a142ac5a691b8960a027a8774ed9/d2433/migration.png 2600w,
/static/aca8a142ac5a691b8960a027a8774ed9/b0096/migration.png 2928w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/7819d6aeba6a92dc04aed5916ffbba9f/b0096/migration-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAPoAAAD6AG1e1JrAAABgElEQVR42pWRz0sCQRTHV3NXNzEVKggWyjwE3bsEZnYNuvkPdCiCgv6BnEOHDiGyF2FglxUXDy6B/ZAFD7GHagkx/FP0aF22+a47thEEDuy+mTff93nvzROE/5bnRRzHiU1GSvtrvGYJ8y6PAWzbjruuK3c6nYym3acYMDEZrb99jhV3LhAqMQwjYZqmwuyGrusrlmWlCSHRsJb5FiilIrTNZjPJzhJiweCaCAuK8X23281CzHxSq9XKhmGAAxiApV6vlwwS+gXNgLjEhwODLFM6EFVVjdfr9Wy4QsDg51UyKyM+SPZTJSCNRiMPMbVoGkLbVuOapqXCmQFnbeZYotVqtSoz/RLvDklmQAwCFSIAzq3j11Th/CFXqxkZ3iKvAklhoS2X/bvILyB+xJlmId62RPqF3cu7s83iha2greDBowBgz5+AvBeVq/7eDvGE6J+W6YCKsNeDUr4yLD6TfunI9z+SRQiDacrT/W0Sd5WPg9PKcP/p5uXQHxylJz7jG/UB4kp//Yp6AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Migration&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/7819d6aeba6a92dc04aed5916ffbba9f/3c492/migration-dark.png&quot;
        srcset=&quot;/static/7819d6aeba6a92dc04aed5916ffbba9f/ff46a/migration-dark.png 325w,
/static/7819d6aeba6a92dc04aed5916ffbba9f/a6d36/migration-dark.png 650w,
/static/7819d6aeba6a92dc04aed5916ffbba9f/3c492/migration-dark.png 1300w,
/static/7819d6aeba6a92dc04aed5916ffbba9f/d544a/migration-dark.png 1950w,
/static/7819d6aeba6a92dc04aed5916ffbba9f/d2433/migration-dark.png 2600w,
/static/7819d6aeba6a92dc04aed5916ffbba9f/b0096/migration-dark.png 2928w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;To support SSL we can copy the &lt;code class=&quot;language-text&quot;&gt;prod-handsonarchitects-tls&lt;/code&gt; certificate Secret from the old cluster:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# us-east1 cluster&lt;/span&gt;
kubectl get secrets prod-handsonarchitects-tls &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; yaml &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; prod-handsonarchitects-tls.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and apply it in the Ingress&apos;s &lt;code class=&quot;language-text&quot;&gt;tls.secretName&lt;/code&gt; of the new cluster:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# eu-central2 cluster&lt;/span&gt;
kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; prod-handsonarchitects-tls.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that at this moment the cluster should not request certificates automatically as the DNS does not point to it. Producing requests by the issuer might quickly drain the &lt;a href=&quot;https://letsencrypt.org/docs/rate-limits/&quot;&gt;Let&apos;s Encrypt certificate requests quota&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;At this moment, the website should be working with the certificate and you may confirm it by adjusting your &lt;code class=&quot;language-text&quot;&gt;/etc/hosts&lt;/code&gt; with:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;213.97.130.13 www.handsonarchitects.com&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Tip: Certificate renewal&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Let&apos;s encrypt certificates are usually issued for 90 days. If you need to renew the certificate before the migration there is a very nice tool for you: &lt;a href=&quot;https://cert-manager.io/docs/reference/cmctl/#renew&quot;&gt;https://cert-manager.io/docs/reference/cmctl/#renew&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It enables to manually trigger the certificate renewal by running this simple command: &lt;code class=&quot;language-text&quot;&gt;mctl renew prod-handsonarchitects-tls&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;dns-switch&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dns-switch&quot; aria-label=&quot;dns switch permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DNS switch&lt;/h2&gt;
&lt;p&gt;Now we can safely switch the DNS to point the new cluster, as we have a working website with the certificate.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/c1628795838dde58938b742282d326e0/b0096/dns-switch.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAABA0lEQVR42n1RSU7EMBDMv7jwCyQewJHfzIVHzB84cUQg5oyYkZCTCDwOjuO1QyUVZkGIVmKVu6qr23Y1/hHCVavrT3WVcxGRn+RZVP8U19vL+u0ihGBtjwBIKf0uhnGew3sPuhS2WgLbruuMMU3TaK0hOwgq0rQYhgFcjBHqOcNPKIW1tZYYMoAKf5oDADS3zjk60hp2WDE2+jO5FAOhj1KKNdPBYuAIywWIYGCUgXKup+mxGMbsPBZxvtTa2f7YGTqwuUxHPU0uY4PAPkl43T+1X82HCZSe6gD2vt52G3Q46wxrvE7rdqvHm5f2fjLK6XDh0y3kCPDwvr57vu2jkTLyUN84eAvB+dOcdgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;DNS switch&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/c1628795838dde58938b742282d326e0/3c492/dns-switch.png&quot;
        srcset=&quot;/static/c1628795838dde58938b742282d326e0/ff46a/dns-switch.png 325w,
/static/c1628795838dde58938b742282d326e0/a6d36/dns-switch.png 650w,
/static/c1628795838dde58938b742282d326e0/3c492/dns-switch.png 1300w,
/static/c1628795838dde58938b742282d326e0/d544a/dns-switch.png 1950w,
/static/c1628795838dde58938b742282d326e0/d2433/dns-switch.png 2600w,
/static/c1628795838dde58938b742282d326e0/b0096/dns-switch.png 2928w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/e6283542db56ac52b8cadab61a127584/b0096/dns-switch-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAPoAAAD6AG1e1JrAAABeklEQVR42mNgwAH+MzAwQpmMPz5KL//1QWr1qlWrmP///8/IQA74/x9iIIj++V7u8O8Pssfnz9/PsWrVbv7Fixfz7d8PYq9iw6H5PyNQAQtIwaJFi7jnz5/PUV9fzwLETMjqQC5cuHCh8LRp08SWLFkiDzRYEqQHpBfZ5YwgzSAGyACgAp6ZM2eyggxdunSpCAPC62B5mCVAdZzbtm3jg8qDHQQ3EGQLCIMUg1wIoidOnMje398vgOxKkMUgcZBLQQZCLYTLwV0JkliwYIEySPHixRP5Vq3qBdo+kX3OnDm8yDZDLVQEebm3t5dz5swOfpjvUCIM6HR2mAtBglZJR3htCtYotbdPEwQpRHchiA4NDWUODQXLMaIYCCLq90PD8L8WW/0pW6vMNZlKTul7pEHiyAaC2LAgqDpvIV17ys60/j8DE4aXZ56ZyQqim884Kject98LVOgLVrSqng0t3TFOnJjLDmLUnLNPqT9nv6XisLcg2IyZaWAzAKL32j55JcURAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;DNS switch&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/e6283542db56ac52b8cadab61a127584/3c492/dns-switch-dark.png&quot;
        srcset=&quot;/static/e6283542db56ac52b8cadab61a127584/ff46a/dns-switch-dark.png 325w,
/static/e6283542db56ac52b8cadab61a127584/a6d36/dns-switch-dark.png 650w,
/static/e6283542db56ac52b8cadab61a127584/3c492/dns-switch-dark.png 1300w,
/static/e6283542db56ac52b8cadab61a127584/d544a/dns-switch-dark.png 1950w,
/static/e6283542db56ac52b8cadab61a127584/d2433/dns-switch-dark.png 2600w,
/static/e6283542db56ac52b8cadab61a127584/b0096/dns-switch-dark.png 2928w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Remember, that DNS switching might take up to 24h to propagate. But this is not a problem, since you have mirrored clusters with your application.&lt;/p&gt;
&lt;h2 id=&quot;cert-manager-installation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cert-manager-installation&quot; aria-label=&quot;cert manager installation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Cert-manager installation&lt;/h2&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/f5830833aca922fbc5518ce12169f571/b0096/install-cert-manager.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAABE0lEQVR42n1RS07DMBDtvdj0FkgcoLsehw2H4A6sWKK27FFBQk6i1nU6cfyd8pxxgVaIUWSN5817bzyZnf4IllOru726TSkz87l4EbN/yM37vNneeO+JBgSSGOM1GcJpCucc4JzFqgaufd8bY9q21VqjjaeoZMAiMY4jsBACuqeKfLUV0kSEBCc6CxlAnAIJYLlaa0VRpCGHE2PDHxWgyKszfJRSwikPC15GqAtgxsCgAbJ2iCF4B7zYFDKExfmU2brcaEvDjzNUgOIsu0hZU+w0OXceO+ViEtm/HVbdsd0Zj8pvsrz54Jqt2ewMHQe8IlVnSOPvdPbj/mXx2j0VoRS/F162kAKS58/Hh/WSvEaz+H0BiCoKri0vUBwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;cert-manager installation&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/f5830833aca922fbc5518ce12169f571/3c492/install-cert-manager.png&quot;
        srcset=&quot;/static/f5830833aca922fbc5518ce12169f571/ff46a/install-cert-manager.png 325w,
/static/f5830833aca922fbc5518ce12169f571/a6d36/install-cert-manager.png 650w,
/static/f5830833aca922fbc5518ce12169f571/3c492/install-cert-manager.png 1300w,
/static/f5830833aca922fbc5518ce12169f571/d544a/install-cert-manager.png 1950w,
/static/f5830833aca922fbc5518ce12169f571/d2433/install-cert-manager.png 2600w,
/static/f5830833aca922fbc5518ce12169f571/b0096/install-cert-manager.png 2928w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/7756837af6cd23d71594125e9e836a30/b0096/install-cert-manager-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAPoAAAD6AG1e1JrAAABmUlEQVR42p1STUsCURSdye9KrYyixCTbRfsgKsvWEQT+gQpCImjdB69w4aZBZiMMSqOOErwkKGI2FbMoCESyf5Lt+li83rFRp8BNDx738t6555573hOELosJgmim4vtb8PyzMX5BKbUxxkThP4uxH0LEj9eJh69G6ElVDTelt35N03yGgZw6uxQzkQPsABSLxT5VVd2EEDvfPVYcFBYKhUAmkxkplUphTjyGGpwLnYkEEcVIQMAv+xVFcYC0XC4PW4G4bzXhOA/ffuTZbHYIdW1Cs4sTYChElGXZlU6nB6wq0RjnUARCs6HABfj57m2Pgot8Pj8FsKbJPkolj67Lrlwu54UVVoW84SRGliTJAy9VQtzX10ovhLSn0XXd1VIIP+c2Hr3ze5VIKpUZNP35pRAxHo/bCGE9M5tXo+sHpTBI2w9CDNNDNu0k1YW5RCURiW3fBXFuJUTesmC/Hgue1JZm15JnoZWdywCl8c6XUmqKAzFZW546rkfvj6qLq80GlDj//DtRlnddSA6fo1vkJXpzakSbPrZEfQN0J9mCUv2/zAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;cert-manager installation&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/7756837af6cd23d71594125e9e836a30/3c492/install-cert-manager-dark.png&quot;
        srcset=&quot;/static/7756837af6cd23d71594125e9e836a30/ff46a/install-cert-manager-dark.png 325w,
/static/7756837af6cd23d71594125e9e836a30/a6d36/install-cert-manager-dark.png 650w,
/static/7756837af6cd23d71594125e9e836a30/3c492/install-cert-manager-dark.png 1300w,
/static/7756837af6cd23d71594125e9e836a30/d544a/install-cert-manager-dark.png 1950w,
/static/7756837af6cd23d71594125e9e836a30/d2433/install-cert-manager-dark.png 2600w,
/static/7756837af6cd23d71594125e9e836a30/b0096/install-cert-manager-dark.png 2928w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The last step is installing the &lt;code class=&quot;language-text&quot;&gt;cert-manager&lt;/code&gt; on EU cluser:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;helm upgrade &lt;span class=&quot;token parameter variable&quot;&gt;--install&lt;/span&gt; cert-manager cert-manager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--repo&lt;/span&gt; https://charts.jetstack.io &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--set&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;installCRDs&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;true &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--namespace&lt;/span&gt; cert-manager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  --create-namespace&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;At this moment, the copied Secret will match the &lt;code class=&quot;language-text&quot;&gt;Certificate&lt;/code&gt; and all should work fine.&lt;/p&gt;
&lt;p&gt;Also, you may remove the old cluster safely.&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/317abf2472d5fbed3ae0b54b830bb0cd/b0096/final-state.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAvUlEQVR42o1RwQ3CMAzsXnzYAokdWIghWIM/TFAeqCmCkOKmTuIknFp4VEI0p+h0snw626nyD6SJdbN9NBuRmFL6Fmeo/pjVZa3qlXOOqAcgQgiL5hlijF3XGWPattVai0gasWBGw/g+rcxMRBDgYRhKk733YIyNfFSstdBFZsRiYNhgsLYP3jtGMKO+bMaSuBMY4UmipnDTxFw29nQhiCer2pzvhl49tpCiZDQF8RDH62F/2pHT+EqJknN+A7AXD+nUKWRpAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Final state&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/317abf2472d5fbed3ae0b54b830bb0cd/3c492/final-state.png&quot;
        srcset=&quot;/static/317abf2472d5fbed3ae0b54b830bb0cd/ff46a/final-state.png 325w,
/static/317abf2472d5fbed3ae0b54b830bb0cd/a6d36/final-state.png 650w,
/static/317abf2472d5fbed3ae0b54b830bb0cd/3c492/final-state.png 1300w,
/static/317abf2472d5fbed3ae0b54b830bb0cd/d544a/final-state.png 1950w,
/static/317abf2472d5fbed3ae0b54b830bb0cd/d2433/final-state.png 2600w,
/static/317abf2472d5fbed3ae0b54b830bb0cd/b0096/final-state.png 2928w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/6d61d27fe36dc69ee3fef02bee72c5a4/b0096/final-state-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 46.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAA/0lEQVR42mNgwAH+MzAwQpmMPz5KL//1QWr1qlWrmP///8/IQA74/x9iIIj++V7u8O8Pssfnz9/PsWrVbv7Fixfz7d8PYq9iY6AEgFy4cOFC4WnTpoktWbJEHmiwJMhQkDgDwkfEg/r6eiYQhhrOCcT8IPbs2bOFgGwecgxkmThxIjvIRSADly5dKgISnzlzJj8Qc5HlwkWLFimCvNzb28sJCsv59fUcmzbN5AKKc5PsbZgLQXRoaChzff1/Jp3kjeJB1UvkQYaSFSmwMKw67yTdeMbB3L95nqxz9jrhVatCyUpSjBMn5rKDGDXn7FPqL9hv6dlvDw7H+v31LCAVAC02eC1em5C4AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Final state&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/6d61d27fe36dc69ee3fef02bee72c5a4/3c492/final-state-dark.png&quot;
        srcset=&quot;/static/6d61d27fe36dc69ee3fef02bee72c5a4/ff46a/final-state-dark.png 325w,
/static/6d61d27fe36dc69ee3fef02bee72c5a4/a6d36/final-state-dark.png 650w,
/static/6d61d27fe36dc69ee3fef02bee72c5a4/3c492/final-state-dark.png 1300w,
/static/6d61d27fe36dc69ee3fef02bee72c5a4/d544a/final-state-dark.png 1950w,
/static/6d61d27fe36dc69ee3fef02bee72c5a4/d2433/final-state-dark.png 2600w,
/static/6d61d27fe36dc69ee3fef02bee72c5a4/b0096/final-state-dark.png 2928w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;In this short note, we migrated the static website from one Kubernetes cluster to another, while using the free Let&apos;s Encrypt SSL certificate and the &lt;code class=&quot;language-text&quot;&gt;cert-manager&lt;/code&gt;. We were able to keep the website online for the whole process of the migration.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Building goal-oriented micro teams based on WebSight]]></title><description><![CDATA[WebSight is a unique place where software engineers can play with modern architecture, edge technologies, and fully agile development…]]></description><link>https://handsonarchitects.com/blog/2023/micro-teams-based-on-websight-io-go-live/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2023/micro-teams-based-on-websight-io-go-live/</guid><pubDate>Wed, 21 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/7ce75d764f774d499f80c8ba9bc5d4fd/5f01d/websight-io-teams-feature.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.07692307692308%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHZLlIGD//EABkQAAIDAQAAAAAAAAAAAAAAAAECABAxEv/aAAgBAQABBQLoltahs//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABoQAAICAwAAAAAAAAAAAAAAAAABEBEhQWH/2gAIAQEABj8Cro8s3Dj/xAAcEAEAAgEFAAAAAAAAAAAAAAABABFRECExQWH/2gAIAQEAAT8hOkVhyQSgnmIGxqTP/9oADAMBAAIAAwAAABBzz//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8QP//EABwQAAIDAQADAAAAAAAAAAAAAAABESFBUTGBof/aAAgBAQABPxBpi694M96UmsQtJlxkCISF1M0+lHkf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;cto-teams-cooperation&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/7ce75d764f774d499f80c8ba9bc5d4fd/01ab0/websight-io-teams-feature.jpg&quot;
        srcset=&quot;/static/7ce75d764f774d499f80c8ba9bc5d4fd/c989d/websight-io-teams-feature.jpg 325w,
/static/7ce75d764f774d499f80c8ba9bc5d4fd/6aca1/websight-io-teams-feature.jpg 650w,
/static/7ce75d764f774d499f80c8ba9bc5d4fd/01ab0/websight-io-teams-feature.jpg 1300w,
/static/7ce75d764f774d499f80c8ba9bc5d4fd/6951b/websight-io-teams-feature.jpg 1950w,
/static/7ce75d764f774d499f80c8ba9bc5d4fd/5f01d/websight-io-teams-feature.jpg 1976w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;WebSight is a unique place where software engineers can play with modern architecture, edge technologies, and fully agile development techniques (experimental development approach, trunk-based development, TDD, pair programming etc.) adequate to their experiences in small, independent teams.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We believe that it is possible to create an extraordinary and unique workplace by building self-organizing small, independent teams that communicate through well-defined contracts and responsibilities.&lt;/p&gt;
&lt;p&gt;In this article, we want to explain how we launched a completely new website using WebSight DXP &amp;#x26; CMS in two weeks.&lt;/p&gt;
&lt;h2 id=&quot;goals&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#goals&quot; aria-label=&quot;goals permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Goals&lt;/h2&gt;
&lt;p&gt;Here are our main goals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;launch new website &lt;a href=&quot;http://www.websight.io&quot;&gt;www.websight.io&lt;/a&gt; in two weeks&lt;/li&gt;
&lt;li&gt;prepare materials, use cases and basic principles for WebSight DXP&lt;/li&gt;
&lt;li&gt;test a new project, the WebSite DXP event streaming platform, in a production environment on Google Cloud Platform&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are our assumptions for all teams:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;one overarching goal for all teams (both business and engineering)&lt;/li&gt;
&lt;li&gt;self-managed and fully isolated teams define their internal plans and criteria for success&lt;/li&gt;
&lt;li&gt;simultaneous development and content entry&lt;/li&gt;
&lt;li&gt;teams of engineers continuously create production-ready versions and independently deploy them to production&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;teams&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#teams&quot; aria-label=&quot;teams permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Teams&lt;/h2&gt;
&lt;p&gt;Both business and engineering teams:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WebSight CMS team (mainly senior back-end engineers) used a simplified SDLC process to deliver a new publishing engine with limited code review, highly functional test automation (e2e)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.websight.io&quot;&gt;www.websight.io&lt;/a&gt; CMS component development team managed by Project Manager using standard GIT flow, limited quality gates, with dedicated Release Manager role and manual testing phases&lt;/li&gt;
&lt;li&gt;designer who created UI designs for all sites&lt;/li&gt;
&lt;li&gt;content authors/information architects focused on site structure and content entry&lt;/li&gt;
&lt;li&gt;infrastructure team (DevOps) using techniques such as trunk-based development, infrastructure as code, ephemeral environments, deployment automation, etc.&lt;/li&gt;
&lt;li&gt;hands-on architects using test-driven development, pair development, and experimental development approaches to ensure platform performance and high availability&lt;/li&gt;
&lt;li&gt;CTO who kept priorities unchanged within two weeks...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The diagram below presents how all teams worked together:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/274786ed9656e2cbbf1b7f1496dc8dd6/f0cf3/websight-io-teams.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.15384615384615%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIDAQX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB7caarjCf/8QAGhAAAwADAQAAAAAAAAAAAAAAAAECECEiI//aAAgBAQABBQJ7Kr0krgUqsf/EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/ASf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAbEAABBAMAAAAAAAAAAAAAAAABABARIQJBwf/aAAgBAQAGPwKFHWrZQyItv//EABoQAAMBAQEBAAAAAAAAAAAAAAERIQAxEEH/2gAIAQEAAT8hJDQvWsmBuIo+/cSBBgUMYPz/2gAMAwEAAgADAAAAEODP/8QAFhEAAwAAAAAAAAAAAAAAAAAAEBFB/9oACAEDAQE/EEo//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHRABAAICAgMAAAAAAAAAAAAAAQARITEQYVGBwf/aAAgBAQABPxC6A8KXNZxBAIir1PXmK12Jw7gIrUJXS/IiADTSnH//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;websight.io teams&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/274786ed9656e2cbbf1b7f1496dc8dd6/01ab0/websight-io-teams.jpg&quot;
        srcset=&quot;/static/274786ed9656e2cbbf1b7f1496dc8dd6/c989d/websight-io-teams.jpg 325w,
/static/274786ed9656e2cbbf1b7f1496dc8dd6/6aca1/websight-io-teams.jpg 650w,
/static/274786ed9656e2cbbf1b7f1496dc8dd6/01ab0/websight-io-teams.jpg 1300w,
/static/274786ed9656e2cbbf1b7f1496dc8dd6/6951b/websight-io-teams.jpg 1950w,
/static/274786ed9656e2cbbf1b7f1496dc8dd6/f0cf3/websight-io-teams.jpg 2456w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;It is worth noting that there are no managerial roles at the team integration level in the above arrangement. Thanks to properly defined contracts, frequent releases, independent staging/production deployments and limited, results-oriented communication, we were able to quickly combine the results of the teams&apos; work and move very quickly.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;The ability to build small, agile teams with limited bureaucracy is crucial for projects that aspire to conquer the market. Building such open work culture is based on strong trust and high professionalism. The transformation is ongoing, but we can already see the effects.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Simple trick to use dynamic values in Helm]]></title><description><![CDATA[This time we will show you a simple trick we found useful when setting up local or ephemeral k8s environments with a valid domain that…]]></description><link>https://handsonarchitects.com/blog/2023/simple-trick-to-use-dynamic-values-in-helm/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2023/simple-trick-to-use-dynamic-values-in-helm/</guid><pubDate>Mon, 19 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/4f58dd7651ef330bddf6dcdeb0ad84c4/e5715/navigating-during-storm.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEz0lEQVR42gHEBDv7AEloYx8uJT5OTX6Kk2JydYeRj5Slo0liW22Cfmd3fLm/x3uGjn6Gj7K4wJado5CWpKmvu4GHi2JvdXSBiQA6VVMmMipOXl6LlZ9cbHV8h4msurRsg3SDjISKkIuus69+hoybo6yttsGHkZyepq+SnadgZ29ze4R8iZEATWReLz82UGBmjpiZgIF2ZV1KXVRAWE49W05DWks+Yk4/Z1BAiHRnl4+Il6Cml6axjJmjZ293dHqDg46ZAFJoZEZWU29yYl1VPz4zJUdIQWNvakdUT1FcXldfY1ZbYk00KkMqJlA6NHNWRKOdko6cpm51gHJ6hH2IkgBJYlVjY09TSjY6PDpMVFxbZmxocHdOUVhQV1pESk1MTlFnTD9qb3duc4FPPURWMyGSgHGBh5N6hI96iY0AS1NAXVM7PUI/TFNYYmlwa3B7e4GMdHqDcnZ9Y2tzaltZc1lVho2Ve4CGcneBV1NeXjstiXZqeYiRe4yKAEs5OEg8L0hVU1dcY2VpcXN4goeLl4uOl5KWn5ibo21STImBhp6jq5aaoI6RmIKFj19UW2xHMIOIgnWKgAA7KCFVODZjXFd5eYCQkp2WmaGdn6ueoKipq6+Ylpt9b3Gur7amp6+ho6ymqLGTk52DhpNpRj54bF9qhHoAQzg0STUuUUQ8ZEtIc1pZko6RmZugd3h3cHBuenl5nJ2jxcXOtba+q6y1r664oaGrnKCrbUlMc2JjeImMAEZFQmBQTGlyeIOEkGtjalxcXlVVU1VRTHh3c3Btanx7eqinq6eepbKrs7GttLCzvJqTnW1DSJCMkoqZnABlcHRTQD9sZ2ugpK2GiZKIi5V+gIRIQTtFPTdRSURtameBfH6WeHyylJylgomui5Shc32agYqlqrKbo6oAdoKHTU5NW0ZEr7K4lJWbhoaOgoSKPzkzS0ZBUktFd3V1r6y1ube+yb7FoXeAwKmwxK+3ycXNurrCpqq0AGZ3cUVUSzwpKIxycYqPlnN4gFVEQmpmaZ2dpKWkq29jXoF2eK2uuKCDjK6Ol9vd5MvK0sK8xrGuu6urugBWbWI4RDo4PzhMMitsS0hhQzpPNzGbprCip666v8eZlZpxWlqSeYGykpze19/U0dbNyM/RzNXEwMy2tcMAYXFvND41P0ZBeox9RzAtTBQLdEcynpmXpaOlt66vnYeHd05NlnV91svT5OHm4Nrf29bb3dvh5OLp4uLpAGN3dik2Kz5APnKHdEdSS0tDQE4tKVUmHWI1K2AxLFw5NGpTWKilrtDN09jT2OLg5urp7u7s8uvr8Ozt8gBQbGMeKxkuNCpNaFA/Sz1NWFFsfXKGiX9bTlFWVlGFoJJ8joyYnqO6t73Mx8zX1trj4ujl5erf3uTY198AOV1HGyQYLTcqQ2BDPEU8P0VBZntsh56RZXBvYHJafaKQdIuEfIqInJ6lsrC3w8PG0dHVz8/VxcXOv8DOACVFKBMbDigwJjpLOTQ5NTpDQFVsXWV8blhoYlZvUoqtmHGCfXaDhomOk4+Ol5OfnJylo56ip5qeqZSYqAAeNR4TFg4cKRcnPiAqKywzOjlJZVFZc2dbcmZVdVdxl3hidm94iImLl5l0eIeBiZJ/iIxydoFwdIBvdoX81kHvHfodMAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;featured image&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/4f58dd7651ef330bddf6dcdeb0ad84c4/e5715/navigating-during-storm.png&quot;
        srcset=&quot;/static/4f58dd7651ef330bddf6dcdeb0ad84c4/ff46a/navigating-during-storm.png 325w,
/static/4f58dd7651ef330bddf6dcdeb0ad84c4/a6d36/navigating-during-storm.png 650w,
/static/4f58dd7651ef330bddf6dcdeb0ad84c4/e5715/navigating-during-storm.png 768w&quot;
        sizes=&quot;(max-width: 768px) 100vw, 768px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This time we will show you a simple trick we found useful when setting up local or ephemeral k8s environments with a valid domain that supports multiple Ingress entries.&lt;/p&gt;
&lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt;
&lt;p&gt;In the current project we are working on we use Kubernetes to run developer&apos;s and production environments to have the closest possible configuration of both. Sometimes developers use local environments, in other cases (when bigger computing power is required) we use ephemeral environments (created for a short time for development) created on-demand in Google Cloud.&lt;/p&gt;
&lt;p&gt;Our system consists of multiple services and some of them are exposed via the &lt;a href=&quot;https://kubernetes.io/docs/concepts/services-networking/ingress/&quot;&gt;Ingress&lt;/a&gt;. To manage the application we use &lt;a href=&quot;https://helm.sh/&quot;&gt;Helm&lt;/a&gt;. To have a near-to-production setup we decided to use &lt;a href=&quot;https://nip.io/&quot;&gt;nip.io&lt;/a&gt;, the &quot;Dead simple wildcard DNS for any IP Address&quot;.&lt;/p&gt;
&lt;p&gt;At some point, we ended with a couple of entries in the Helm&apos;s &lt;code class=&quot;language-text&quot;&gt;values.yaml&lt;/code&gt; such as:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;serviceA&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ingress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;127&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;1.nip.io
&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;serviceB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ingress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;127&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;1.nip.io
&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;serviceC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ingress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;127&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;1.nip.io
&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;serviceD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ingress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; d&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;127&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;1.nip.io&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For the local environment, it was working just fine. However, when we started to use ephemeral environments in GCP it required overriding values in a couple of places, to match the GKE cluster&apos;s external IP address.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;   helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;token parameter variable&quot;&gt;--set&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;serviceA.ingress.host&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;a-34-117-173-194.nip.io &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;token parameter variable&quot;&gt;--set&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;serviceB.ingress.host&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;b-34-117-173-194.nip.io &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;token parameter variable&quot;&gt;--set&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;serviceC.ingress.host&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;c-34-117-173-194.nip.io &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;token parameter variable&quot;&gt;--set&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;serviceD.ingress.host&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;d-34-117-173-194.nip.io &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
      my-app &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; values.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It would be much simpler to pass the ingress IP as a single value and use it in all the configurations. Until now, Helm does not support dynamic values in &lt;code class=&quot;language-text&quot;&gt;values.yaml&lt;/code&gt; with some good reasoning for that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/helm/helm/issues/2492&quot;&gt;https://github.com/helm/helm/issues/2492&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/helm/helm/pull/6876&quot;&gt;https://github.com/helm/helm/pull/6876&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h2&gt;
&lt;p&gt;Fortunately, we were able to use one of &lt;a href=&quot;https://helm.sh/docs/howto/charts_tips_and_tricks/#using-the-tpl-function&quot;&gt;Helm&apos;s tips and tricks&lt;/a&gt; to do the work in that case.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;tpl&lt;/code&gt; function allows developers to evaluate strings as templates inside a template. This is useful to pass a template string as a value to a chart or render external configuration files. Syntax: &lt;code class=&quot;language-text&quot;&gt;{{ tpl TEMPLATE_STRING VALUES }}&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now, the Ingress config in our project&apos;s chart could use the &lt;code class=&quot;language-text&quot;&gt;tpl&lt;/code&gt; function:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; networking.k8s.io/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Ingress
&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; tpl .Values.serviceA.ingress.host . &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;pathType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Prefix
        &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;backend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; serviceA
            &lt;span class=&quot;token key atrule&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And we were able to leverage the chart&apos;s default &lt;code class=&quot;language-text&quot;&gt;clusterIp&lt;/code&gt; like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;clusterIp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 127&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;serviceA&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ingress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; .Values.clusterIp &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;.nip.io
&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;serviceB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ingress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; .Values.clusterIp &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;.nip.io
&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;serviceC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ingress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; .Values.clusterIp &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;.nip.io
&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;serviceD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;ingress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; d&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; .Values.clusterIp &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;.nip.io&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With this config, overriding the local &lt;code class=&quot;language-text&quot;&gt;127.0.0.1&lt;/code&gt; with the cluster&apos;s external IP is as easy as running:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;   helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;token parameter variable&quot;&gt;--set&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;clusterIp&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;34&lt;/span&gt;-117-173-198 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
      my-app &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; values.yaml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice, this config will work well if you don&apos;t use dynamic values in the &lt;code class=&quot;language-text&quot;&gt;service.ingress.host&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;In this short guide, we combined:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nip.io/&quot;&gt;nip.io&lt;/a&gt;, the &quot;Dead simple wildcard DNS for any IP Address&quot;,&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://helm.sh/docs/howto/charts_tips_and_tricks/#using-the-tpl-function&quot;&gt;Helm&apos;s &lt;code class=&quot;language-text&quot;&gt;tpl&lt;/code&gt; function&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result was a simpler way to configure Kubernetes external cluster&apos;s IP in multiple Ingerss entries.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Apache Pulsar client prefetch mechanism trap]]></title><description><![CDATA[When adding new technologies to our portfolio, we spend some time experimenting with its core features to "eliminate guesswork". The more…]]></description><link>https://handsonarchitects.com/blog/2023/apache-pulsar-client-prefetch-mechanism/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2023/apache-pulsar-client-prefetch-mechanism/</guid><pubDate>Sat, 20 May 2023 15:40:32 GMT</pubDate><content:encoded>&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/5236c548291a3294da3841e0e12c3fb7/0a758/pulsar-client-prefetch-mechanism.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 30.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsTAAALEwEAmpwYAAABZElEQVR42o2PzUoCYRiF5wKC1m28hi6gn1XQVbSRiP5W7YU2RdG2bNMftiqCdrUozYokJFEwnRE0bZokbX50dMbSmafRWQUtOh/n+97zLZ7DK3wYMi9aFlETKWk5pE+JoppHUiUv5ymoBXK1LFX1lb56rsuF4hIpOjzW3cGff/sSqlqKtbd7AukE62KGwLFCKJMjEKmwmsowEnlnO5vguXRDp2XzalgMhWsIm1Umz3Qf6JU4nvuvoKhpNuQHlksJdvNpgjGRw7LE1M45Swd7TB/F2Iqs8aJmcb976FaX0ZMGw/saM1HTo/UGoG636wONlk6jKfPVUmi23rCsCtV6gXa7gmpUsL2s1Eo02w06lk24XGYh8cHClc58VOOy2CSfeSI4O0symUTgD7mO+zu7fm6YJiGxwOLdOyvROnPXGqdphcfbGGPjE8TjcQRv838dv8jx4I439d0b2Lbb6LqBLMuYpskPrVaqTxyfHbwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;topic-subsription-client-diagram&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/5236c548291a3294da3841e0e12c3fb7/3c492/pulsar-client-prefetch-mechanism.png&quot;
        srcset=&quot;/static/5236c548291a3294da3841e0e12c3fb7/ff46a/pulsar-client-prefetch-mechanism.png 325w,
/static/5236c548291a3294da3841e0e12c3fb7/a6d36/pulsar-client-prefetch-mechanism.png 650w,
/static/5236c548291a3294da3841e0e12c3fb7/3c492/pulsar-client-prefetch-mechanism.png 1300w,
/static/5236c548291a3294da3841e0e12c3fb7/d544a/pulsar-client-prefetch-mechanism.png 1950w,
/static/5236c548291a3294da3841e0e12c3fb7/d2433/pulsar-client-prefetch-mechanism.png 2600w,
/static/5236c548291a3294da3841e0e12c3fb7/0a758/pulsar-client-prefetch-mechanism.png 3604w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/6601ccde4fb5339372dc43c31d7f35e6/0a758/pulsar-client-prefetch-mechanism-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 30.76923076923077%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAPoAAAD6AG1e1JrAAABgUlEQVR42mNwjNMJMwqSWWgYpzfVJFx5ukG49nSTMFUwbRQG5k8xjtRZ5J9p7csAAjNnsrIv++/Buux/DMvS/2ZgMYb/jAwwYOrFvVpnUsZ/o1f3/yhVZvwXn3f7v1xp0X/phXf+K5fm/Rdf/OSPdmH4f/9kreUg9c5LLymJbfr/WXLf//9iG/8fABvHwMD4//9/MGYw8xJcqtOb/l9vWccf5fTw/wr1E/9LJfj/c5+98X9Yb+t/y2kbfme2J/w3D1ecAdKsMfmasNCy35eEVvz+JLL81xKwgUCD9v//zwI20LfAXdveQyTCzoIt0ClQMtDDmzM0OFk33iuAP8g3VjXI25sz0DfdMjK00Fcl3z9ewPrixRrF7a8XSM5+tVBq4duFiu2HYr5cOWLw4fPnOa9fvzZlIAr8h4RRQlGRrOWV69NUNzxfpDDr8Wzp+S+XmnVsK3h//YTjx89fDr97984epI6xvp6BiRBmQA4nJPD0zBmukydPCu/atUu1tbVVHABjKbfxvoCEjwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;topic-subsription-client-diagram&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/6601ccde4fb5339372dc43c31d7f35e6/3c492/pulsar-client-prefetch-mechanism-dark.png&quot;
        srcset=&quot;/static/6601ccde4fb5339372dc43c31d7f35e6/ff46a/pulsar-client-prefetch-mechanism-dark.png 325w,
/static/6601ccde4fb5339372dc43c31d7f35e6/a6d36/pulsar-client-prefetch-mechanism-dark.png 650w,
/static/6601ccde4fb5339372dc43c31d7f35e6/3c492/pulsar-client-prefetch-mechanism-dark.png 1300w,
/static/6601ccde4fb5339372dc43c31d7f35e6/d544a/pulsar-client-prefetch-mechanism-dark.png 1950w,
/static/6601ccde4fb5339372dc43c31d7f35e6/d2433/pulsar-client-prefetch-mechanism-dark.png 2600w,
/static/6601ccde4fb5339372dc43c31d7f35e6/0a758/pulsar-client-prefetch-mechanism-dark.png 3604w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;When adding new technologies to our portfolio, we spend some time experimenting with its core features to &quot;eliminate guesswork&quot;. The more advanced the tool, the more time we need to discover its behaviours. Working with &lt;a href=&quot;https://pulsar.apache.org/&quot;&gt;Apache Pulsar&lt;/a&gt;, a distributed streaming platform, we explore event-steaming architecture whose complexity requires us to verify empirically many aspects.&lt;/p&gt;
&lt;p&gt;This is exactly how we approached the next challenge, which was to read only the latest messages for a given key from the topic. In our case, these are &lt;code class=&quot;language-text&quot;&gt;published pages&lt;/code&gt;, of which only the latest versions are processed. A similar situation may apply to &lt;code class=&quot;language-text&quot;&gt;stock exchange data&lt;/code&gt;, where we are only interested in the current prices of listed companies.&lt;/p&gt;
&lt;p&gt;The mechanism that &quot;cleans up&quot; the old messages per key is called &lt;a href=&quot;https://pulsar.apache.org/docs/3.0.x/concepts-topic-compaction/&quot;&gt;topic compaction&lt;/a&gt;. Once a topic compaction completes, all consumers assigned with active subscriptions should receive only the latest messages per key.&lt;/p&gt;
&lt;p&gt;With such a scenario, our test should:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;produce the appropriate number of messages with the same keys and send them to the topic&lt;/li&gt;
&lt;li&gt;invoke the compacting mechanism on the topic&lt;/li&gt;
&lt;li&gt;wait for the compaction to finish&lt;/li&gt;
&lt;li&gt;read the next message on the consumer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our code looks like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Producer&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; producer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;newProducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;topicName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

producer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
producer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
producer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
producer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;4&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;B&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;Consumer&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; consumer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;newConsumer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;topicName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unit-test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readCompacted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;waitUntilCompacted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;topicName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;assertNextMessageValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; consumer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;assertNextMessageValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;4&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; consumer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We expected that only the compacted data would be available to the consumer after compaction was complete. But the tests result was different:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Expecting: 3
Consumed: 190:0:-1:0 value: 1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Our hypothesis formed based on Pulsar documentation seemed wrong. We immediately remembered the Richard Feynman words:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If your guess disagrees with experiment, then it (your guess) is wrong!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We spent more time experimenting with our failing test. We quickly discovered that the Pulsar client has a prefetch mechanism that downloads events from the topic in larger batches. By default, the client downloads &lt;code class=&quot;language-text&quot;&gt;1000&lt;/code&gt; messages at a time, and each time &lt;code class=&quot;language-text&quot;&gt;receive()&lt;/code&gt; is called, we get a message previously downloaded from the topic. Although after compacting the topic, the messages may already be &quot;stale&quot;.&lt;/p&gt;
&lt;p&gt;To limit this behavior we can configure the &lt;code class=&quot;language-text&quot;&gt;receiverQueueSize(int receiverQueueSize)&lt;/code&gt; option on the consumer:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The consumer receive queue controls how many messages can be accumulated by the Consumer before the application calls Consumer.receive(). Using a higher value could potentially increase the consumer throughput at the expense of bigger memory utilization.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For the purpose of testing and learning about Pulsar, we recommend setting this value to &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt;. This will ensure that only the first event is &quot;stale&quot;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Producer&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; producer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;newProducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;topicName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

producer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
producer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
producer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
producer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;4&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;B&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;Consumer&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; consumer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;newConsumer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;topicName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unit-test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readCompacted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;receiverQueueSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;waitUntilCompacted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;topicName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;assertNextMessageValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; consumer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;assertNextMessageValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; consumer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;assertNextMessageValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;4&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; consumer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Our modified test case was successful. We experimentally proved our expectations regarding the compaction mechanism.&lt;/p&gt;
&lt;p&gt;More details &lt;a href=&quot;https://github.com/handsonarchitects/sandbox/blob/main/pulsar/src/test/java/com/handsonarchitects/pulsar/PulsarClientReceiverQueueSize.java&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Fixing Docker Desktop when all other methods fail]]></title><description><![CDATA[Docker Desktop is a great tool that helps to save a lot of time during local development. However, it is not perfect and can break down in…]]></description><link>https://handsonarchitects.com/blog/2023/fixing-docker-desktop-when-all-other-methods-fail/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2023/fixing-docker-desktop-when-all-other-methods-fail/</guid><pubDate>Tue, 25 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/699d34e9b342623cdbff8c223f217f74/e5715/broken-docker.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 100%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFmElEQVR42h2UC1DU1xXG/2psOnFaK21qhFg1ERIU0OVN8AUrj+WxwMICy65seCwry3tXQJbIG1wDGFCQh9gSNIoYg4nBRCD4SmLABdHEgjStsZnOJFPSZCS2SZ3x12vvzJlz586d853z3e9+Uv+Zs1Q3vE6GMZfYRC07I+JQqjTo07JIfTULjS6dSKUKeWgU/oHByLwCCFcoMWbnUd9g4+SpU0zdnGZh4QGPHz9GclrvwYtufnj472SD9zZ8AncSEpXAZt/tbA2OEns1UXEawkSOiNcRHpuM05qXeeY3q1j+u+dZsfIPPLfaGQ+ZP1mmAiTfoEgUaj2Z+WU0tXRxuLsP2xsdHD89yOxfv+bKp5OcFPv9ze2kZuYSl5JJQfl+5NGJbPAKRBYQhOvmV3Ba58ryZ1cj7TIWYrRUkP9aA+r0PORRGrzk8YQmG8jIt/JabSNdff2MfGLn2sQkHT295JY1YDtyAu/AEBzXbcTZ3Q8XD39ecPVCiteb0GXkkJ1jYbfRRId1D/sKS8kuKkMhinoHKQVIIimGHCpfb2Xg/EXOvn8Ra+1BkkQDzqKQi0cALqKos7sv0tYoHSZrI9/9sMB/Hy7wzdQ1vr19g4EPrrAjwUR0aiGxOhOqVCOJ6TnosgqptrVw8uwQNQda2BYag6uvHM9tCjYFyJFU5oPstmiw28d4soYzFGhdPHDZohEUFKAr3I+qoIGwTCvRejPhmhx8I/QEhCdTVF5PdWMbkUkZ+ATHEhCagJRZeYzxbB++sumYaSviHZUPkf4x+G2NJ1u2nrzAILQV7Rgr21BXH0df10Xuvn0U1jRhrhJyq++lpmeQsoYWkrPMSObmfv6m3MRChg/fp7jyQ6Y3lMp5ZJbzpt9atMpIzKFbyVAmUnykl4Fj++ioyORcr42poWr21taQZm1h14HTZAkwae/QJFvcNhDt5EDSmpUoVzmw+2VHqmRrMa//NaXbnsOm8iI3MpAzjaFMtbrTbYmmpzKWzto0+mvjOVqrpe7wIfSdg0jls/9hY0wKiySJ5UuWsEzkJ/EraTGrlj6N4+KnWCktwlGcrXtqMZ4rlhCx+heoZE5EeLhj2L6WCo0bTWo3LOVC2KNfz9N89I+owsJQRytJjlXi+ZILjsue4dmlS3F2WEGwzJO0pESKjFmEefnxwi+X8XsB8FsRDk/yokUCVMJntRD24dOn6HjvXXqGhjgzMsyPP/3Eo59/5vbNKW5NTvLFnTvMP3jAI6GAhz8ucP/+Pbr7eskwGFCrVKgU4WgS4rGYLRhKi5HSayoJNhkx1NVQd6SN4tZWbH/qpe/C+xS3NLMjfRfeyRpU5j0ce/cc33z/HfP/fshbIyMUH27DaDtAbGkZ4SV7SWo+gJTf0kRl35t0i8ujn3xMn+j2zMVhzl+9xqG3jpNaYkFTVEDIq6koso10vX2WWzN/5u7cHJftN+gZHKSqs5NiEYXtrUjXxUiz9+5hn53lnasfU3m0i8qOdhL3WMisqKKkqRG9dS+68jI0paWEGrLYlKIhtKSE+Lp6Kk6e4MOJCWb//hVvfzSM5KnREqDfhac2BXe1Gjd1HG4JKny1OpyjFKzauZ31ygg2ahIIzDOR193N4KSdcxPjXBA+2D92iYmZmf9zzBM/9E9Lw0erxT02Gm9NIr5itLhyK2ktb6AWvEaIzmIqKkg7dJCB61c5P/oRBms58pQU/BSROG2W8eKOIPT1Ni59/oUwh8JcfI3pBOXlIC/II8ySj6qmmvTOdqoGBhi4fIUTY5cZGh9nSNhXSLYJB08Za7a8wvNb/HlJEYJLTCR+YsJoi/h6Y3dnGJ62M3brJje+vMv0/b9wZeZzLtjHOSceadQ+Qf/oRU6NjjD46Wdcun2L6S/nmJ6b5frtm4L7O0zMzfCP+X/y7b/m+R9Bjq+Vv7BMzgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;broken-docker&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/699d34e9b342623cdbff8c223f217f74/e5715/broken-docker.png&quot;
        srcset=&quot;/static/699d34e9b342623cdbff8c223f217f74/ff46a/broken-docker.png 325w,
/static/699d34e9b342623cdbff8c223f217f74/a6d36/broken-docker.png 650w,
/static/699d34e9b342623cdbff8c223f217f74/e5715/broken-docker.png 768w&quot;
        sizes=&quot;(max-width: 768px) 100vw, 768px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Docker Desktop is a great tool that helps to save a lot of time during local development. However, it is not perfect and can break down in ways that are hard to restore, especially when you are using an M1 Apple silicon processor...&lt;/p&gt;
&lt;p&gt;Today we would like to share a short script that helps with uninstalling Docker Desktop from MacBooks. The tool offers several &lt;a href=&quot;https://docs.docker.com/desktop/troubleshoot/overview/&quot;&gt;options to troubleshoot&lt;/a&gt;. Unfortunately, there are times, when everything fails (including &lt;code class=&quot;language-text&quot;&gt;purging data&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;resetting to factory defaults&lt;/code&gt;, or even &lt;code class=&quot;language-text&quot;&gt;uninstalling&lt;/code&gt;), especially after quite extensive container deployments, uninstalls, scale-ups, and especially down (don&apos;t try to scale your k8s pods to &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;We use the following script to uninstall the Docker Desktop for Mac when the options mentioned above fail. It needs the administrator privileges to run:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-x&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-Rf&lt;/span&gt; /Applications/Docker.app
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /usr/local/bin/docker
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /usr/local/bin/docker-machine
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /usr/local/bin/com.docker.cli
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /usr/local/bin/docker-compose
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /usr/local/bin/docker-compose-v1
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /usr/local/bin/docker-credential-desktop
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /usr/local/bin/docker-credential-ecr-login
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /usr/local/bin/docker-credential-osxkeychain
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /usr/local/bin/hub-tool
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /usr/local/bin/hyperkit
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /usr/local/bin/kubectl.docker
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /usr/local/bin/vpnkit
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-Rf&lt;/span&gt; ~/.docker
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-Rf&lt;/span&gt; ~/Library/Containers/com.docker.docker
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-Rf&lt;/span&gt; ~/Library/Application&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; Support/Docker&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; Desktop
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-Rf&lt;/span&gt; ~/Library/Group&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; Containers/group.com.docker
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; ~/Library/HTTPStorages/com.docker.docker.binarycookies
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /Library/PrivilegedHelperTools/com.docker.vmnetd
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /Library/LaunchDaemons/com.docker.vmnetd.plist
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-Rf&lt;/span&gt; ~/Library/Logs/Docker&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; Desktop
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-Rf&lt;/span&gt; /usr/local/lib/docker
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; ~/Library/Preferences/com.docker.docker.plist
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-Rf&lt;/span&gt; ~/Library/Saved&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; Application&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; State/com.electron.docker-frontend.savedState
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; ~/Library/Preferences/com.electron.docker-frontend.plist

&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-rf&lt;/span&gt; ~/Library/Group&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; Containers/group.com.docker/pki/
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-rf&lt;/span&gt; ~/.kube&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It is a product of Reddit, StackOverflow and other sources research and we use it 2-3 times a month.&lt;/p&gt;
&lt;p&gt;After removing Docker Desktop using the script above, install the new application as usually.
We hope it will help some of you.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Docker Apple M1 | M2 architecture support in Apache Software Foundation projects]]></title><description><![CDATA[With new M1 / M2 MacBooks from Apple, the "new"  architecture began to dominate the laptop market. In this short article, we describe how…]]></description><link>https://handsonarchitects.com/blog/2023/apache-sling-starter-with-m1-macbooks-support/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2023/apache-sling-starter-with-m1-macbooks-support/</guid><pubDate>Sat, 22 Apr 2023 17:40:32 GMT</pubDate><content:encoded>&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/b061108ffd2f2faa2c30fcdeec6d5971/b0805/m1-docker-in-asf.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 24%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsTAAALEwEAmpwYAAABCElEQVR42lVQwUrDQBDtL+Wab/CmvyJ+iH6EUiztSYVSpSdFED20EsGqTbWhmCZxs9lN0pnnZjdKOzDMzNuZeW+nA2PM2DEhBOq6bqv2Ua3AYtFCvDu0lXe6wRrH90skubZAfzCA7/tIkgRSSmitLF6cHKJ6HqFUBIpjUKFAPwJsIittsLXZy+jsdQMcnAX4Fm7wvNeD53mIoghZksK0QwwfEO4fgT5ClMMxyosRdP8S9eME9dMU5dUNyvGtU1gRY0NOMhEhTVPERkGWZf/fmJ3eYXU9xUZL5LM55OILxecS8uUV4j2EfJsjnwSgRqE7AdtL2bjljoShVOVybsjJElv/q5tovOn9BdSZdtte1AsRAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;asf-docker-apple-logos&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/b061108ffd2f2faa2c30fcdeec6d5971/3c492/m1-docker-in-asf.png&quot;
        srcset=&quot;/static/b061108ffd2f2faa2c30fcdeec6d5971/ff46a/m1-docker-in-asf.png 325w,
/static/b061108ffd2f2faa2c30fcdeec6d5971/a6d36/m1-docker-in-asf.png 650w,
/static/b061108ffd2f2faa2c30fcdeec6d5971/3c492/m1-docker-in-asf.png 1300w,
/static/b061108ffd2f2faa2c30fcdeec6d5971/d544a/m1-docker-in-asf.png 1950w,
/static/b061108ffd2f2faa2c30fcdeec6d5971/d2433/m1-docker-in-asf.png 2600w,
/static/b061108ffd2f2faa2c30fcdeec6d5971/b0805/m1-docker-in-asf.png 4081w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/0c4a650401a257d55fae6557679987ea/b0805/m1-docker-in-asf-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 24%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAPoAAAD6AG1e1JrAAABSElEQVR42mNgAIP/jAxI4OXLlzz79+9nAcv8ZwDL/Z/LIPpvIqcMmM3AwAjCDNiA3rbPEUYbvxbk7/8vAOJ/+vTJH4iPffjwQfDIkSO8jx4d4wSJv/cWnfGhQMjNnmE/xycJFdH/Fhacb1TM+J5KGnP9l5fneCZhIBoaGsrMYLjj1zbTrX/WZu/+LwzW+P59MNDA80BXSpxctxss9l+zyPmnesyGdxwS8l8k9d1+iBh4fRXVC/oiomn4U1Rb/4uIrudncX0HsAuNz/xnDV31nxnErq+vZzp58qTwhQsXxC5duiQID4LgjfGfPZY7T7Fn4DnhFKp1xCNA+YSrr+JZcw+9PR6hWmfsfTTOmnsb29fbs0CDEBiG/yHh+B9I/4ewGSGW/Gda1P2cG8z+X89kX1/PErpqFTMM1wPDuv7/fyaV3Fx2eXt7DgDzX41JY+LzoAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;asf-docker-apple-logos&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/0c4a650401a257d55fae6557679987ea/3c492/m1-docker-in-asf-dark.png&quot;
        srcset=&quot;/static/0c4a650401a257d55fae6557679987ea/ff46a/m1-docker-in-asf-dark.png 325w,
/static/0c4a650401a257d55fae6557679987ea/a6d36/m1-docker-in-asf-dark.png 650w,
/static/0c4a650401a257d55fae6557679987ea/3c492/m1-docker-in-asf-dark.png 1300w,
/static/0c4a650401a257d55fae6557679987ea/d544a/m1-docker-in-asf-dark.png 1950w,
/static/0c4a650401a257d55fae6557679987ea/d2433/m1-docker-in-asf-dark.png 2600w,
/static/0c4a650401a257d55fae6557679987ea/b0805/m1-docker-in-asf-dark.png 4081w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;With new M1 / M2 MacBooks from Apple, the &quot;new&quot; &lt;code class=&quot;language-text&quot;&gt;arm64&lt;/code&gt; architecture began to dominate the laptop market. In this short article, we describe how developers could help the open-source ecosystem to adopt their Docker images to the new platforms.&lt;/p&gt;
&lt;p&gt;Developers with M1 / M2 MacBooks using Docker need support for the new &lt;code class=&quot;language-text&quot;&gt;arm64&lt;/code&gt; architecture processors. For project maintainers, it means that instead of a single &lt;code class=&quot;language-text&quot;&gt;amd64&lt;/code&gt; platform image, they must build several once for &lt;code class=&quot;language-text&quot;&gt;amd64&lt;/code&gt; / &lt;code class=&quot;language-text&quot;&gt;arm64&lt;/code&gt; platforms. It requires adopting their current CI tools to build multiple images and expose them within a single Docker manifest (under a specific tag name). This process is time-consuming and requires much effort, especially in open-source world.&lt;/p&gt;
&lt;p&gt;With &lt;a href=&quot;https://github.com/rombert&quot;&gt;Robert Munteanu&lt;/a&gt; from Adobe, we adopted one of &lt;a href=&quot;https://www.apache.org/&quot;&gt;Apache Software Foundation&lt;/a&gt;&apos;s (ASF) projects, &lt;a href=&quot;https://github.com/apache/sling-org-apache-sling-starter&quot;&gt;Apache Sling Starter&lt;/a&gt;, to the new multi-arch support procedure.&lt;/p&gt;
&lt;p&gt;It required the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create an &lt;a href=&quot;https://issues.apache.org/jira/browse&quot;&gt;Apache JIRA issue&lt;/a&gt; asking for M1 Docker images support e.g. &lt;a href=&quot;https://issues.apache.org/jira/browse/SLING-11714&quot;&gt;SLING-11714&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Agree with the ASF project owner to use &lt;a href=&quot;https://github.com/features/actions&quot;&gt;GitHub Actions workflows&lt;/a&gt; that have the support for &lt;a href=&quot;https://docs.docker.com/engine/reference/commandline/buildx/&quot;&gt;docker buildx&lt;/a&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;I would suggest using GitHub Actions, there you can use the official suite of docker actions to build multiplatform images with little need for custom scripting [1].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;small&gt;source: &lt;a href=&quot;https://lists.apache.org/thread/xd3p8nom68gkgjmjpgfylh0vyx260s5o&quot;&gt;https://lists.apache.org/thread/xd3p8nom68gkgjmjpgfylh0vyx260s5o&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;
&lt;p&gt;Provide PRs with a working solution (in our case, we use &lt;a href=&quot;https://github.com/fabric8io/docker-maven-plugin&quot;&gt;Docker Maven Plugin&lt;/a&gt; to manage &lt;code class=&quot;language-text&quot;&gt;buildx&lt;/code&gt; container that builds and pushes images):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/apache/sling-org-apache-sling-starter/commit/1b879e7eec280e1bd20a4bc1e247f29604f26294&quot;&gt;https://github.com/apache/sling-org-apache-sling-starter/commit/1b879e7eec280e1bd20a4bc1e247f29604f26294&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/apache/sling-org-apache-sling-starter/commit/39e9cd35e5b4bb0c4a639fe42950555ca03f05c3&quot;&gt;https://github.com/apache/sling-org-apache-sling-starter/commit/39e9cd35e5b4bb0c4a639fe42950555ca03f05c3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/apache/sling-org-apache-sling-starter/commit/019999478ab600ab0aef111aac110f44f8a19bdb&quot;&gt;https://github.com/apache/sling-org-apache-sling-starter/commit/019999478ab600ab0aef111aac110f44f8a19bdb&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The Apache project owner needs to raise an issue to Apache Software Foundation infrastructure team to enable GH actions secrets for pushing docker hub images e.g &lt;a href=&quot;https://issues.apache.org/jira/browse/INFRA-23983&quot;&gt;INFRA-23983&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once all ready, PRs can be merged.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Summarizing, adding support for the &lt;code class=&quot;language-text&quot;&gt;arm64&lt;/code&gt; platform in open-source projects is not a complex work. Satisfaction from being the &lt;a href=&quot;https://github.com/apache/sling-org-apache-sling-starter/graphs/contributors&quot;&gt;ASF contributor&lt;/a&gt; is worth spending a few hours after your everyday routines.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How Technology Radar helps harness the tech stack at different levels]]></title><description><![CDATA[Technology Radar is a phenomenon that helps visualize the technology stack. By the technology or the technology stack, we mean not only…]]></description><link>https://handsonarchitects.com/blog/2023/how-technology-radar-helps-harness-the-tech-stack-at-different-levels/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2023/how-technology-radar-helps-harness-the-tech-stack-at-different-levels/</guid><pubDate>Mon, 17 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1016px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/7d816e3b983ae9b907d8002e109533d9/f4281/radar.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 96.9230769230769%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAAsTAAALEwEAmpwYAAAE40lEQVR42k1UaWxUVRS+xUJE9FeN0ZBgTIyJ/tHEhEQEBMWEmJCAuCSu8YfyQxSmQ2mNID9MTIwmihFoASstRWEKtcB09s5MZ6YtbWfrdLY3b2bebJ196bS0tJ3l87xh0Zu8d2/OPfc723cOg5U9vmJln8LOXmC0urq+WCvuAB4a8iae0wVL+wan45Lro7zU7JjdpyWZjO5EHYPB0Czuyzb2fAPDy1oYomwblhmqseYTDSDry2uVXOZthT9jUvPFZVNyBerkMuJDHNA3DeN0alkezJs0pAO81gCsJpq/RY18iLKtDE72RNywXpJQs5d6gQ1Kf6p/OHIbushCXcnlahouV7nB5yre685K4eKtisGVqmlit6GPLEDpT8sGc3gsbmYvpsfWHV4gLDG0JtHKwES5RRUoWUSPFFxuVR3IVW76svVLMxl0WhP4fSKCHlcSokxDdwouu2pKrkLDFy0DE4mWe2lqot+JNWCsSRuKXbGk50Dhrqi4XL3bkcJxSxStxggO6YI4pOVxxBhtyLqdKZBB8jC7bE6tQhvMXxExRCwRmN30Yt9kWY+pxR+runAQp6wZSIxhSI0C2s1RHDWE0aYPooPOUjLQSvIztkQDlKKp6KOUIr60l91f2tAd+a3iJCaL3dW/vSG0GqJoGxHQYYrgsF7Ax/948dE1Dw7R+RtzBFJDHAd1SfROZ6EJZKuG+BK0oZKiATaSvf2UOlScU/gLkLvz9R/GE+Sd0PDmgCqIV845sfm0Fds6rdjV48LnCgE/2W24FO7HL3YPRkKl+tJKBYH8Ur7LOruJqbnidmUgT7nLYtCXqXeYBLQRoEQfxgdXvRgTijg1FsWrnTbs+MOJN3s4nPcrwUOKk9MWyH1l5IvFmie7iH5fYQu7PCrsHfKR655UXeaI1w+rOUipAAfkfuzvc6JQKuOaI47NJ8ex86wVO885cEw/g1/NJrRr3eizxTFoF2oKLo/Lk9E9TM7Nb7dE51BZrWAmVa4fGYmgzUTJp++tv9zY/acDu7vtBGTHjm4X3qCwTdEyFpeAn6eSkM0Uoebma4bYIoZ58lDG5TcahblyNFeGPVasfz8Wo6IIOE5hfzkUwM4LM9jaZcO2M1PYdcGFz4Z4yDxJTETS+G1qFrq4sW5Km4iP+aLcU3y6URh1qDSkERag8merva402glQOD+GIH0HqbKfDPjwIVX5K10Y7VR5Mb8HKS09zhxs811VN7qhDecU/9EmMr/fQgQlotaUxK1O6yxGe8cxenESR8wxHCVOilw8SmmQkDGR7F32JDSUtyGfu2ZOxaEL5d9hD9qFliZc7B/LgRQyFZGw5+yzDc61joidEsLX5JF4Pma+2ymau8xYFd9oggv9/2u9e73sTbSo+ML4aKZKirkqDYWa3J9DnyuDM5NxnJ6IoY/6Wi4ygqNISMeSqUHFFcbFtw8A79xiz9TsTWdp6rz+LrBO6UkOjCTugNhfV/MFaAP5utqXrpK8quXzDZl4ZyQdhW/2miSG9eLbiqPpPM3WTQwczcNSE+qBNd81rBhYM4W8R+HPGmiirAxH5jFMvaqloolnkq+Q3KAgHYA1hgGENR1YIAd5toVl3OxRj/zh7Q4Z23jX7XsTg5bKnXmWKv/+jemk5KpVkCr5wnvXSXb//r6uW/7IkzPy5i0pJ9vwL4QbLUDmTm+qAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;empty-technology-radar&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/7d816e3b983ae9b907d8002e109533d9/f4281/radar.png&quot;
        srcset=&quot;/static/7d816e3b983ae9b907d8002e109533d9/ff46a/radar.png 325w,
/static/7d816e3b983ae9b907d8002e109533d9/a6d36/radar.png 650w,
/static/7d816e3b983ae9b907d8002e109533d9/f4281/radar.png 1016w&quot;
        sizes=&quot;(max-width: 1016px) 100vw, 1016px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Technology Radar is a phenomenon that helps visualize the technology stack. By the technology or the technology stack, we mean not only frameworks, languages, and platforms, but also techniques, skills and everything that might be the subject of work required to deliver the software. In the Technology Radar, the particular technologies are called &lt;em&gt;blips&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Let&apos;s dive into how the tool may help to harness the tech stack at various levels of business starting with some of the most popular definitions:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An opinionated guide to technology frontiers&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;small&gt;source: &lt;a href=&quot;https://www.thoughtworks.com/radar&quot;&gt;https://www.thoughtworks.com/radar&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A technology radar is a way of observing the market for new innovations and technologies and gather information about them in a consistent style, relate and evaluate them on behalf of the own business.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;small&gt;source: &lt;a href=&quot;https://www.igi-global.com/dictionary/technology-roadmap-living-labs/29542&quot;&gt;https://www.igi-global.com/dictionary/technology-roadmap-living-labs/29542&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;technical engineering documentation that works as a portfolio for you to visualize an institution&apos;s technologies and methodologies, monitor new trends, and identify which tools need to be removed, either because the technology is legacy.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;small&gt;source: &lt;a href=&quot;https://dzone.com/articles/what-is-tech-radar-why-teams-need-to-have-one&quot;&gt;https://dzone.com/articles/what-is-tech-radar-why-teams-need-to-have-one&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;context-is-the-king&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#context-is-the-king&quot; aria-label=&quot;context is the king permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Context is the king&lt;/h2&gt;
&lt;p&gt;Radars get more traction recently in the technologists community. As members of the community, we find it very useful to represent the technology stack at different levels, e.g. personal development, project or product technology stack, company-specific framework choices, or even industry trends.&lt;/p&gt;
&lt;p&gt;Depending on the level, radar may represent different categories (quadrants) or priorities (rings). A specific situation we use the radar to visualize creates the &lt;strong&gt;context&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;dimensions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dimensions&quot; aria-label=&quot;dimensions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dimensions&lt;/h2&gt;
&lt;p&gt;As mentioned above, depending on the &lt;em&gt;context&lt;/em&gt;, different metrics could be visualized on the radar. That means, that one company can have more than one radar (which often is the case). It is simple to imagine that one radar could represent the technology portfolio of the company, while another could represent the skills the company is looking for in the new candidates and the current engineering team.&lt;/p&gt;
&lt;p&gt;However, there are some common qualities, the radar helps to understand no matter the context:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;usefulness&lt;/code&gt; - how useful we find the technology in the &lt;em&gt;context&lt;/em&gt;, e.g.:
&lt;ul&gt;
&lt;li&gt;on the personal radar (one that helps to organize what you know and what you want to learn) it may point to what we want to learn next,&lt;/li&gt;
&lt;li&gt;on the company radar, we may seek information on what conferences/workshops we should send employees to,&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;confidence&lt;/code&gt; - how much do we trust (but also know) the tech, e.g.:
&lt;ul&gt;
&lt;li&gt;the project&apos;s radar shows that one technology is the default choice for some kind of platform services (e.g. we choose &lt;code class=&quot;language-text&quot;&gt;Quarkus&lt;/code&gt; over &lt;code class=&quot;language-text&quot;&gt;Spring&lt;/code&gt; when implementing backend services),&lt;/li&gt;
&lt;li&gt;on the personal radar, placing the blip may represent our seniority in the technology or technique (e.g. we are just getting to know &lt;code class=&quot;language-text&quot;&gt;Apache Kafka&lt;/code&gt;, but we could lead the training in the middle of the night on adopting &lt;code class=&quot;language-text&quot;&gt;Continous Integration&lt;/code&gt;),&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;trends&lt;/code&gt; (or &lt;code class=&quot;language-text&quot;&gt;adoption&lt;/code&gt;) - how widely adopted is the technology, how much did we invest in it already, is it gaining or losing popularity, e.g.:
&lt;ul&gt;
&lt;li&gt;the digital experience industry radar may show an increasing trend in measuring their applications footprint (e.g. clients ask more and more about the &quot;Green Cloud&quot;),&lt;/li&gt;
&lt;li&gt;the personal radar may represent the technology we know well, but we don&apos;t want to invest in/work with anymore (e.g. we are experts in &lt;code class=&quot;language-text&quot;&gt;Maven&lt;/code&gt;, but it let us down so many times, that we will start new projects only using &lt;code class=&quot;language-text&quot;&gt;Gradle&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;In this article, we showcased some scenarios of working with Technology Radars. The plural form is not a coincidence. We work with the tool for several years now. We find it extremely useful because of its simplicity and depth at the same time (the separate concern is putting it together - but that&apos;s the case for another, much longer article). We are using it for personal development and adopting it in projects and companies we are working with. It is a great tool, that does not require much introduction while it shows complex things (the technology stack) in a simple and accessible form.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Code quality dimensions]]></title><description><![CDATA[The aforementioned post Thinking About Code Review was triggered by one of the Twitter threads on CI/CD vs PRs. The especially interesting…]]></description><link>https://handsonarchitects.com/blog/2023/code-quality-dimensions/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2023/code-quality-dimensions/</guid><pubDate>Thu, 13 Apr 2023 22:40:32 GMT</pubDate><content:encoded>&lt;p&gt;The aforementioned post &lt;a href=&quot;https://tidyfirst.substack.com/p/thinking-about-code-review&quot;&gt;Thinking About Code Review&lt;/a&gt; was triggered by one of the Twitter threads on CI/CD vs PRs.&lt;/p&gt;
&lt;p&gt;The especially interesting part was &lt;em&gt;&quot;Dimensions of Variability&quot;&lt;/em&gt; where two dimensions of inspecting the code were mentioned: &lt;code class=&quot;language-text&quot;&gt;blocking/non-blocking&lt;/code&gt; vs &lt;code class=&quot;language-text&quot;&gt;synchronous/asynchronous&lt;/code&gt;. The original author&apos;s conclusion was:&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/40fb04df8e35d869e1299b7425c5fc22/373fb/dimensions-of-variability.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.15384615384615%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB+klEQVR42nVTTWsUQRCd36snIQcluetdowRE1ICXGAQJgSB6EoJGRMFk3Zhdd7OT/cpkMzvT0zP9Vc+aD3d23FhM0UW/7tdVr2o8rBhVi2BvgVybPWliFe70D5BtcSwX297/CI0+x3iwian/GM6MlrC/D44x7D1k/BHH0wXu1YeoQejsGPOrHUSzXc7icoXQ6ktc9F+yb8OaYJnwZiNyUDKCVbJZagOPmTht4B5RyOkMeG/SAAwliKkLgd8cp2VW1taSQCJCB3PqLPCC0GRHkOETGPkmL5SPlxdmpo13/ho+DO8isr1iL8vSIrPcYurhMNrAx/AeYterCnbwdPoV4XQLSbjbIAxtB+/9DRz01jE3/ZJQZTWh6+PT/AH7fQgaVISsobMRonkHIvbLZjjHTlBWYBL/xMX1MZRJmAhI07QoO+fMjMAwPMFUnEJbWdwhuqEpxhgorXhsDIvGr1oUsdYaQohizf1fXClVJOOROYVN9+HUYZV0WbIgH8fiGVpyG5LKsVFLJSd2xNhztLMcX5pDk31BfLWJLN6pNeQvRhd7nTs46K8hwfmKhrPsF96e3cJe9zZC3a2botIzXAf7SJPPzQx5sL8Hr3ASvoZ0QdXlrNApN+mm+Ba8wNHoKRIzqZuSHzDGgWj1D3QsU5EQ1foum2P9rKHG3P8B7ifotZjUuzEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dimensions-of-variability&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/40fb04df8e35d869e1299b7425c5fc22/3c492/dimensions-of-variability.png&quot;
        srcset=&quot;/static/40fb04df8e35d869e1299b7425c5fc22/ff46a/dimensions-of-variability.png 325w,
/static/40fb04df8e35d869e1299b7425c5fc22/a6d36/dimensions-of-variability.png 650w,
/static/40fb04df8e35d869e1299b7425c5fc22/3c492/dimensions-of-variability.png 1300w,
/static/40fb04df8e35d869e1299b7425c5fc22/d544a/dimensions-of-variability.png 1950w,
/static/40fb04df8e35d869e1299b7425c5fc22/373fb/dimensions-of-variability.png 2068w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/5e48bddca954b81740ef13238556479d/373fb/dimensions-of-variability-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.15384615384615%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAPoAAAD6AG1e1JrAAACq0lEQVR42m1TS08TURS+tKXTKVOmReTVBinCSmJCAkQEqUXEPlVqpqEDrVOpndJXhBZFGhyDQUqkgQqaoiZEDZv5C+wMv8Cwc6FxqSu3LrzeO3TqUDzJzT35vjnfPa8BoMogADX4ng2DzigHliIh8Cga1LXLHITHfGQaWBNhzROeUz3mQ8CsjD1hDAPU+J5P6D3LD5vgUqYRPogRozIn88kIGBUWW+DyAuJ5MFSJxS8KAlDhoxQMs1pPNGyAkRAF706pr1YLhoOULRVrhUm+FSJ/WBlbbVLa1y8217kGu0fGelv6HF2A+B9v7+8YuYx42zmgq7CxadCD+uS/HwRjcpbYAhtUI1sAdh867jXahLHd3WgtKPdw9jVtYl+p7WyRsLNlXnoszddu5jIkTPGabyhlrRzAbZlc8ZIZRrdbYegFNYKx9XcLBkGwabDvf6Ye44r1cGbbCP2rhF0qWUQlx2eIzVyWhmme+u5woNLKgv4VvSuyZYb8yw4YWDXaJMH1BYOtLHg7R4yza0bI5mnozalHK4I3nWTfhLdtecJjieCURVGUGutImS3jnCXr5KxLbr5bWot8Pk8LgqDF/kSqy+KcsWZuhNsS3rS1GWOIU52aCBLUFgoFUhCOtP+wI+3e3p5uf3/fhDnsV/OiWCAhhCoQ51TJ+D315zhX89FmAxq55MBz3WBwnT6cyhsOJldqL2BsZ0egGJGRKghu0L2IO5xcIz/5V+t6jjNEQ52LEYWnixTMJnVflUPxrQBX+m0nTJTa/zAbYFgWlIcSLTVdy3w4D+ffW2HsTeOVSg8DPmoxwBh/T97RHygzdCapIW+66Yc71fDFkdL3VmfoniMHbs2d/enLtv3yZM70KxYbfyCgxTzV0BoLYEiEa2SgWCwS4MT/eolsAAP1Suwvj7bjgnWb0BkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;dimensions-of-variability&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/5e48bddca954b81740ef13238556479d/3c492/dimensions-of-variability-dark.png&quot;
        srcset=&quot;/static/5e48bddca954b81740ef13238556479d/ff46a/dimensions-of-variability-dark.png 325w,
/static/5e48bddca954b81740ef13238556479d/a6d36/dimensions-of-variability-dark.png 650w,
/static/5e48bddca954b81740ef13238556479d/3c492/dimensions-of-variability-dark.png 1300w,
/static/5e48bddca954b81740ef13238556479d/d544a/dimensions-of-variability-dark.png 1950w,
/static/5e48bddca954b81740ef13238556479d/373fb/dimensions-of-variability-dark.png 2068w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This categorization looks really accurate. It took us only a couple of minutes of discussion to bring the &lt;a href=&quot;https://martinfowler.com/articles/ship-show-ask.html&quot;&gt;Ship / Show / Ask&lt;/a&gt; strategy.&lt;/p&gt;
&lt;p&gt;When we tried to put ship/show/ask techniques into the &lt;em&gt;&quot;Dimensions of Variability&quot;&lt;/em&gt; we got the following result:&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/ed139682bfdaaf1433bf9596fa444a38/07d12/ship-show-ask.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.23076923076924%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB3UlEQVR42n1STW/TQBTM/7/BBamCA5dWQkhISC0HBFRVD/REEiK1Ahrc0LSOW8cbf2Ttfft2eGsH25VbLD17vbMev5l5Izx1uUJqLnUlVfWB3SOFs7/geCEv1KKjR5jqu6UFouULxLcvAY57WIM7O8dN8Bwqei1vWYs/ScgUIrnbh7p7I10kA0JTBgh+7mEZ7MtW/j/C5mJnkG9XKM1ajtkhzhUKvYIhJTj3JHt2F8K5uPOn1izrrS9u1jUJd0eIG8wXd9+N2HyH3uyhyt8KKbUf8DrH+tMY6stU/Nf1XlVVIr/phsIEt0dnUCcziIyOkMqZ+PQKOn3XpNUjjD5/Q3Q8AWdDQhMpLI6+IjweC2EvZUsp8uw3yKxqyV4WOwaV4lEYo4pTsCHp3kFrDWut4A6kS+QeX2cyEba1YxAKEcFURswmsU6InZW1kR8aFEVRd2nMDhcGkjMe93v+pyOmOVgfwplTL7RjzkvY2R/Y8xtAU0/ybg6TAmZ6Bf4Rin7bk1yOkd0/Q5ke+Gi7FDcawftTLD+cSZLmkVAULg4+4trjDzw0C6j7Q6TJSdPhv1AkCDW5xPbiWpiGHbIkv5nMkUwvW7wmFNkgmTNr3WB4Lds6oL6/3qcOF4/54dD/BR2u8d1Mo5DSAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ship-show-ask&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/ed139682bfdaaf1433bf9596fa444a38/3c492/ship-show-ask.png&quot;
        srcset=&quot;/static/ed139682bfdaaf1433bf9596fa444a38/ff46a/ship-show-ask.png 325w,
/static/ed139682bfdaaf1433bf9596fa444a38/a6d36/ship-show-ask.png 650w,
/static/ed139682bfdaaf1433bf9596fa444a38/3c492/ship-show-ask.png 1300w,
/static/ed139682bfdaaf1433bf9596fa444a38/d544a/ship-show-ask.png 1950w,
/static/ed139682bfdaaf1433bf9596fa444a38/07d12/ship-show-ask.png 2093w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/768316f7a324812dab9ea32cda2272e0/07d12/ship-show-ask-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.23076923076924%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAPoAAAD6AG1e1JrAAACqUlEQVR42m2TzU/acBjHH3lpUZHSDQR8ocNpMSabTowRdUO3CUUL3YsV3FaRgkDAupFF51wyku1oPHAx4eSdw45c9gd4mNnJ7D8wMTs5s8s2k98o2sqYz6Ftnm9+n+f3PM+3AP9Hk/zIikAlBVhZEUBIJOCaoiF0rueSQKeXNOn0EiyKIrTVn/0neB608ltKY9zWawvaeEn+yqVgWM4VCqBR9FxCEy9s2tH6mukkm4BeRVerKpWVA0tRLJxbMaNMnPgtRnUjjcCU0BbbeOVEa5mOUyECdD3wypbnnhEkt+gKMbzF52GhpVGfeWxrn410cw95cpJhAFfV5AtwVWcVEAXwKtXl4IAy3wVi2AfWoRmwtZ7foKBDF0Av2NrHwDQ6AeZBBnovgatJXeFtHkdSSn9cBRqVPIvdfBQlbyGeGDhjdZT3AmhMeVJ6+Xu+xR2P2T1ogRg4CUHX+Qyh2nJaxD9s5gmUTRq+h8PqtmAWo55ErUNowXL7LGjomWgEhtr6Y7HuMRSxD/8IgotWgU/DxB1uzrLFBlsjcss8X661HbC5b0xZ6TfjJirD2Iescq5YLJokSaq1N93hdvts9OYk0RPnqEEzQqgJXWWb0kFJX97ZaS6hA72SKx8eYpViEd/d3SVLpVLL3t6eoV4vViq4nJOhVQPrl7Oi9ltmuemTVLctP3SMs1rnPquhPvvB0S/ntre3Wws+n642Yy0VDGGuL7MaZ4WBzi7VAesS9v7jOyPaWMOP8nloVoABcHCJbi+KOUb++ME2qgDVpeDu+Cp9Hy13jp4G4LpbneHzeTwpRMw/hUVyv95PD3Bqyk/SR/eNPV/vQWffJdBTA/q0TnaG6DtmLP2H02CnGn49vnqzmKHR4F38WDPUebNcLmO1OV1Er8TgDtZTb3r4C5mqxY+i8aWqAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ship-show-ask&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/768316f7a324812dab9ea32cda2272e0/3c492/ship-show-ask-dark.png&quot;
        srcset=&quot;/static/768316f7a324812dab9ea32cda2272e0/ff46a/ship-show-ask-dark.png 325w,
/static/768316f7a324812dab9ea32cda2272e0/a6d36/ship-show-ask-dark.png 650w,
/static/768316f7a324812dab9ea32cda2272e0/3c492/ship-show-ask-dark.png 1300w,
/static/768316f7a324812dab9ea32cda2272e0/d544a/ship-show-ask-dark.png 1950w,
/static/768316f7a324812dab9ea32cda2272e0/07d12/ship-show-ask-dark.png 2093w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The quarter worth a few words is the &lt;code class=&quot;language-text&quot;&gt;non-blocking&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;synchronous&lt;/code&gt; one. We treat &lt;em&gt;show&lt;/em&gt; as a kind of knowledge sharing (with additional benefits like feedback on the solution/code). On the one hand, it does not block proceeding with taking the changes further. On the other hand, the session that is valuable for both sides (the audience and the presenter) is synchronous. It requires gathering in one space (physical or virtual) at one time to show and get feedback. Of course, the form could be asynchronous too. However, our previous knowledge-sharing sessions tell us that the greatest chance for the presenter to get feedback is during or just after the &lt;em&gt;show&lt;/em&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Exclusive subscription messages order with Apache Pulsar]]></title><description><![CDATA[Pulsar message order Be aware that negative acknowledgments on ordered subscription types, such as Exclusive, Failover and Key_Shared, might…]]></description><link>https://handsonarchitects.com/blog/2023/apache-pulsar-exclusive-subscription-message-order/</link><guid isPermaLink="false">https://handsonarchitects.com/blog/2023/apache-pulsar-exclusive-subscription-message-order/</guid><pubDate>Sun, 12 Mar 2023 22:40:32 GMT</pubDate><content:encoded>&lt;h2 id=&quot;pulsar-message-order&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#pulsar-message-order&quot; aria-label=&quot;pulsar message order permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Pulsar message order&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Be aware that negative acknowledgments on ordered subscription types, such as Exclusive, Failover and Key_Shared, might cause failed messages being sent to consumers out of the original order (&lt;a href=&quot;https://pulsar.apache.org/docs/2.11.x/concepts-messaging/&quot;&gt;source&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this blog post, we try to explain the above sentence with two examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;messages are positively acknowledged and processed in sequential order (the sequence identifier is pre-assigned by the producer)&lt;/li&gt;
&lt;li&gt;some messages are acknowledged negatively and re-delivered with some delay.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;positive-acknowledge&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#positive-acknowledge&quot; aria-label=&quot;positive acknowledge permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Positive Acknowledge&lt;/h2&gt;
&lt;p&gt;The following diagram illustrates how the consumer receives and acknowledges the messages sent by the producer. Our consumer subscribes to the &lt;code class=&quot;language-text&quot;&gt;unit test&lt;/code&gt; topic with the subscription type &lt;code class=&quot;language-text&quot;&gt;Exclusive&lt;/code&gt; and the subscription initial position &lt;code class=&quot;language-text&quot;&gt;Earliest&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Consumer&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; consumer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newConsumer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;STRING&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;topicName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscriptionName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;unit-test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscriptionInitialPosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SubscriptionInitialPosition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Earliest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscriptionType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SubscriptionType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Exclusive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/f51f7e8b9f97baae020fcb960a37b252/81471/exclusive-subscription-acknowledge-initial.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.61538461538461%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABRElEQVR42pWRyUrDUBSG81q6dOWi+galay3uXAgOIPgCrnwAH8Fpo5uKgnYQLJUW0dohadK0N02aqc3wmRREikXxO9zFOfxnukeK45ggCAjDENuz0K0PhNNmaH+9VuJ36Iomnu+yiOl0OstPa0mmaaKqPcTQQjPrnL7es3LW4rBcJXPe4KBcY/WiyWP3kq6pQLOBn1vGzi4x2cow1br0hUBTVRzHQbJtG62vYYgxA6vB8XOJ3M0bR5Uam4U6e8UX8rcNnpQr2sMOfr2GyK+jb6wx2skyljsMDQO938fzPKR0zCiKiKMY17cRYzlZrcfIlhmIdyxHSeIKiiFD5HJSddmtBOyXPLaLE+5kP1k6JEzyUyR+IY5+RJIGk6SpS1sdYOiCoFSYU0jz8r/tW5ngjokfrmfHWFjwv6R/pijK7MIpURTzCVIeC5PoRWi4AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Exclusive subscription backlog state before messages acknowledge&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/f51f7e8b9f97baae020fcb960a37b252/3c492/exclusive-subscription-acknowledge-initial.png&quot;
        srcset=&quot;/static/f51f7e8b9f97baae020fcb960a37b252/ff46a/exclusive-subscription-acknowledge-initial.png 325w,
/static/f51f7e8b9f97baae020fcb960a37b252/a6d36/exclusive-subscription-acknowledge-initial.png 650w,
/static/f51f7e8b9f97baae020fcb960a37b252/3c492/exclusive-subscription-acknowledge-initial.png 1300w,
/static/f51f7e8b9f97baae020fcb960a37b252/d544a/exclusive-subscription-acknowledge-initial.png 1950w,
/static/f51f7e8b9f97baae020fcb960a37b252/d2433/exclusive-subscription-acknowledge-initial.png 2600w,
/static/f51f7e8b9f97baae020fcb960a37b252/81471/exclusive-subscription-acknowledge-initial.png 3306w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/c257c3c2709af78eddc5182376dc4a88/81471/exclusive-subscription-acknowledge-initial-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.61538461538461%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAPoAAAD6AG1e1JrAAABf0lEQVR42mOor69nmjhxIjsDEHinm6vahEqnWgWJJliGSCVahogn2gBp80DxRKd4o4SYYlcxBghgZEACV65cYdu/fz/L////GRkOHz4suGbLGiUgh9U2VKRWM8Pjv2Tfjt+Kxfn/5btW/pcvKvon1b3+r1mk7H/bAgf//6oM0v/MuY7+t+U/8t+MY9ObQF2Znc8/i506eFAWaCgPw5EjR3hXbVolBzSQxTZIoFItN/a/TP2MPwr5uf/lqzqBBpb9k6vs/GcaLPHfo9A18L+XueZbe8l9Hx1l97yzlVx7IMFbdf+NGyJ7Nm+WPnPmDBfY+atWrWIGMQJz7WUcohT8bf3YvB0jZP38YpQi7INFfOwD2Hyc0ux8/zMwsIgu+p7NteZ/Pf/K/w3cq/+3Kcx5ZwfSG7rqPzMDOSCyfqlIfNcWiaalx1Xb0+sV3pvxO+FU/P8/A2N9PQMTDKPz4eqgkfJfi4Hntwm7CzgyqAF27tzJvWXLFiVQDAO5TBYWFpwAcfmdrSm3nswAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Exclusive subscription backlog state before messages acknowledge&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/c257c3c2709af78eddc5182376dc4a88/3c492/exclusive-subscription-acknowledge-initial-dark.png&quot;
        srcset=&quot;/static/c257c3c2709af78eddc5182376dc4a88/ff46a/exclusive-subscription-acknowledge-initial-dark.png 325w,
/static/c257c3c2709af78eddc5182376dc4a88/a6d36/exclusive-subscription-acknowledge-initial-dark.png 650w,
/static/c257c3c2709af78eddc5182376dc4a88/3c492/exclusive-subscription-acknowledge-initial-dark.png 1300w,
/static/c257c3c2709af78eddc5182376dc4a88/d544a/exclusive-subscription-acknowledge-initial-dark.png 1950w,
/static/c257c3c2709af78eddc5182376dc4a88/d2433/exclusive-subscription-acknowledge-initial-dark.png 2600w,
/static/c257c3c2709af78eddc5182376dc4a88/81471/exclusive-subscription-acknowledge-initial-dark.png 3306w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Then it receives unacknowledged messages (the set of unacknowledged messages is called backlog) one by one and acknowledges/confirms them:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; m1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; consumer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
consumer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;acknowledge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; m2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; consumer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
consumer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;acknowledge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; m3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; consumer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
consumer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;acknowledge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The order of received messages reflects the initial (sequential) order: messages &lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;B&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;C&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; m1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;B&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; m2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; m3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each &lt;code class=&quot;language-text&quot;&gt;receive&lt;/code&gt; command moves the subscription cursor to the next position (from right to left). Finally, the cursor is positioned after the &lt;code class=&quot;language-text&quot;&gt;C&lt;/code&gt; message:&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/dea96e4a3eaa11036a35dbba772e0576/81471/exclusive-subscription-acknowledge-final.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.61538461538461%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABR0lEQVR42n2RS07DMBRFsy0YMmJQWAGfOVRsAISExAZYA0vgN2JABwgBBQkoaoRoKW1D0o/buEmTJrEPCbRIRYVrWXq2/I7vtQ2tNXEckyQJXiBpyzeE/07Xm8xauq7TEFWCcMgsRVH01Z+xDNd1se0PRFfiuGUOXy5ZOKqxV3wkd2yyWyyxeFLlunFKw7WgYhKszSNX5hjlc0R2g5YQOLaN7/sYnufhtBx6YkBHmhw83LJ+/sr+XYmNQpmdm2c2Cyb31hm1bp3EfGK4tYzMLxFurzJKgZ1ej3arRRAEGJlNpRRaaYahhxg002gf9L0mHVFB+la6b+H0m0Rp5EEwojtKsP2QdhgTphA1jpvJ4B9pNSm+D9O2UA9XRGkpHJs4DNBxNNUzBdR/jZScIVWzii5ejC9TM0386/C3pMw+0PmJN2Xm6+k0nzyIDmrK3yTxAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Exclusive subscription backlog state after messages acknowledge&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/dea96e4a3eaa11036a35dbba772e0576/3c492/exclusive-subscription-acknowledge-final.png&quot;
        srcset=&quot;/static/dea96e4a3eaa11036a35dbba772e0576/ff46a/exclusive-subscription-acknowledge-final.png 325w,
/static/dea96e4a3eaa11036a35dbba772e0576/a6d36/exclusive-subscription-acknowledge-final.png 650w,
/static/dea96e4a3eaa11036a35dbba772e0576/3c492/exclusive-subscription-acknowledge-final.png 1300w,
/static/dea96e4a3eaa11036a35dbba772e0576/d544a/exclusive-subscription-acknowledge-final.png 1950w,
/static/dea96e4a3eaa11036a35dbba772e0576/d2433/exclusive-subscription-acknowledge-final.png 2600w,
/static/dea96e4a3eaa11036a35dbba772e0576/81471/exclusive-subscription-acknowledge-final.png 3306w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/2333f7f284587b0c890ad516c2bb54fc/81471/exclusive-subscription-acknowledge-final-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.61538461538461%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAPoAAAD6AG1e1JrAAABiUlEQVR42mOor69nmjhxIjsDEHinm6vahEqnWgWJJliGSCVahogn2gBp80DxRKd4o4SYYlcxBghgZEACV65cYdu/fz/L////GRkOHz4suGbLGiUgh9U2VKRWM8Pjv2Tfjt+Kxfn/5btW/pcvKvon1b3+r1mk7H/bAgf//8oMsr8tuU/8sRM+/tecc8sTf23Znc8/i506eFAWaCgPw5EjR3hXbVolBzSQxTZIoFItN/a/TP2MPwr5uf/lqzqBBpb9k6vs/GcaLPHfPd896L+dtOo3K75tP+1Ft/4y51l6L8hQftutZ6JHdu2SOnPmDBfY+atWrWIGMQJz7WUcohT8bf3YvB0jZP38YpQi7INFfOwD2Hxc0sy8KzJtBP37zwvon/8vEHrov6jp7v/CHWnl/P9XhTKDgo6BWPAfGmb/jRkk3xrzWDP8/8+8e8IEuZ0xMdxXtBjYcGv8z8BYX8/ABMMw/qpQBrAP/pvzK/wy4bAHsWFiFIEdO3YI7d69Ww6b90JDQ5nt7e05AP1XnDQwj91eAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Exclusive subscription backlog state after messages acknowledge&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/2333f7f284587b0c890ad516c2bb54fc/3c492/exclusive-subscription-acknowledge-final-dark.png&quot;
        srcset=&quot;/static/2333f7f284587b0c890ad516c2bb54fc/ff46a/exclusive-subscription-acknowledge-final-dark.png 325w,
/static/2333f7f284587b0c890ad516c2bb54fc/a6d36/exclusive-subscription-acknowledge-final-dark.png 650w,
/static/2333f7f284587b0c890ad516c2bb54fc/3c492/exclusive-subscription-acknowledge-final-dark.png 1300w,
/static/2333f7f284587b0c890ad516c2bb54fc/d544a/exclusive-subscription-acknowledge-final-dark.png 1950w,
/static/2333f7f284587b0c890ad516c2bb54fc/d2433/exclusive-subscription-acknowledge-final-dark.png 2600w,
/static/2333f7f284587b0c890ad516c2bb54fc/81471/exclusive-subscription-acknowledge-final-dark.png 3306w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;All acknowledged messages are marked for deletion. Read more about &lt;a href=&quot;https://pulsar.apache.org/docs/2.11.x/cookbooks-retention-expiry/&quot;&gt;conditions&lt;/a&gt; when messages are deleted from the topic.&lt;/p&gt;
&lt;h2 id=&quot;negative-acknowledge&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#negative-acknowledge&quot; aria-label=&quot;negative acknowledge permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Negative Acknowledge&lt;/h2&gt;
&lt;p&gt;The order of messages changes when messages are acknowledged negatively. In the following scenario, the message &lt;code class=&quot;language-text&quot;&gt;m1&lt;/code&gt; is negatively acknowledged and rescheduled for re-delivery in the future:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;producer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
producer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;B&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; m1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; consumer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
consumer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;negativeAcknowledge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; m2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; consumer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
consumer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;acknowledge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
producer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; m3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; consumer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
consumer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;acknowledge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; m4 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; consumer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;receive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
consumer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;acknowledge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m4&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The figure below shows the above scenario:&lt;/p&gt;
&lt;div class=&quot;diagram-light&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/dfa3dba6494494acb30223ed42b8542e/81471/exclusive-subscription-negative-acknoledge-initial.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.61538461538461%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABTklEQVR42o2Ru0oDQRSG97X0IVLaiFqKCiLBys7C3srnsAuCijfQKAhi3HiJURZz2dmdyW4yk5i9fWZVxICK/3A4U/zzn/MxVpqmxHFMHMUoLUb1jK8dOsb56qLn0FIOufcnRVFEkiRkWYYVhiGu69JqC16CU1bL10zv1Vg4vGH+qMrs/h3F81vsVglpBvBU5XVuEj0zQbRcYOg2EFLhttsYY7C01niehxA+dXXB0kmVtXKVhaM7Ni4rzO7es2VfYTd2cH3J8L6CXCogFguY1Slip4YXhHhCMBgMsPI1c5Q0SQmMIjQNzGsL1XWQwfP7vdtv4qomOggoORErZ0OKNtQ31+Fgm2SEnSXxO77FL8oH5fVdHd8n7GoanR7tywv6x6VRUDLmGQvM/jrZx4c81h5oCo+kbhM/VD4fpj8H/kdSSnSvR2eEL5UaI0rTjDfzUQmGHhhuYAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Exclusive subscription backlog state after message negative acknowledge&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/dfa3dba6494494acb30223ed42b8542e/3c492/exclusive-subscription-negative-acknoledge-initial.png&quot;
        srcset=&quot;/static/dfa3dba6494494acb30223ed42b8542e/ff46a/exclusive-subscription-negative-acknoledge-initial.png 325w,
/static/dfa3dba6494494acb30223ed42b8542e/a6d36/exclusive-subscription-negative-acknoledge-initial.png 650w,
/static/dfa3dba6494494acb30223ed42b8542e/3c492/exclusive-subscription-negative-acknoledge-initial.png 1300w,
/static/dfa3dba6494494acb30223ed42b8542e/d544a/exclusive-subscription-negative-acknoledge-initial.png 1950w,
/static/dfa3dba6494494acb30223ed42b8542e/d2433/exclusive-subscription-negative-acknoledge-initial.png 2600w,
/static/dfa3dba6494494acb30223ed42b8542e/81471/exclusive-subscription-negative-acknoledge-initial.png 3306w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;diagram-dark&quot;&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1300px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;https://handsonarchitects.com/static/764308e575a28be3bed03c8bebc4a961/81471/exclusive-subscription-negative-acknoledge-initial-dark.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.61538461538461%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAPoAAAD6AG1e1JrAAABkklEQVR42mOor69nWrVqFdv///+ZHBIN7C0ChdOtQqUSLANFEy3DZBKtgoQSLMLkUtzTzIOAahgZsIArV66w7d+/nwUsv2PHDqGDBw/KPv/8X8zMn/uAYlXNf+mm2X8U6zr/K9T1/Jdrmv5XtqL+v2WwyLv6//8F/mswqP035zz035b/yH8zro2PA81kdj5/LnYKaAbQUB6Gbdu28e3atUvq06f/Iib+onvlatr/yxdX/ZGr6v6vmJfzX65++j+ljMj/QBe/mbVtv8xLRwX9t/aSez84y+75ZiWw/ru9jMqmp59E9mzeLH3mzBkukIsZ6//XM4EYzhmWhnahQoF2/rxenpFywd6xKqF2/lxedqFifj5ZDs776+tFBJf99+BZ8b+bYff/iiu+ltP/GzH4gPT+r7dnYSAV7JozR6qkfopE2qq7crtiw9zfucr67rfHY9D//0DX1jMwYccQXzy5flH9zv//Yj9shLQ+24npgvUxMDAxkAuePHki/PLlS55V2w6Jbjx+XBwmHhoaymxvb88BAPiQrXUkWz6qAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Exclusive subscription backlog state after message negative acknowledge&quot;
        title=&quot;&quot;
        src=&quot;https://handsonarchitects.com/static/764308e575a28be3bed03c8bebc4a961/3c492/exclusive-subscription-negative-acknoledge-initial-dark.png&quot;
        srcset=&quot;/static/764308e575a28be3bed03c8bebc4a961/ff46a/exclusive-subscription-negative-acknoledge-initial-dark.png 325w,
/static/764308e575a28be3bed03c8bebc4a961/a6d36/exclusive-subscription-negative-acknoledge-initial-dark.png 650w,
/static/764308e575a28be3bed03c8bebc4a961/3c492/exclusive-subscription-negative-acknoledge-initial-dark.png 1300w,
/static/764308e575a28be3bed03c8bebc4a961/d544a/exclusive-subscription-negative-acknoledge-initial-dark.png 1950w,
/static/764308e575a28be3bed03c8bebc4a961/d2433/exclusive-subscription-negative-acknoledge-initial-dark.png 2600w,
/static/764308e575a28be3bed03c8bebc4a961/81471/exclusive-subscription-negative-acknoledge-initial-dark.png 3306w&quot;
        sizes=&quot;(max-width: 1300px) 100vw, 1300px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The order of received messages for our example is:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; m1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;B&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; m2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; m3&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;A&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; m4&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSequenceId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; m4&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSequenceId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The consumer receives message &quot;A&quot; with some delay (second attempt). We can make sure it&apos;s the same message by checking its sequence ID.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;Apache Pulsar delivers messages in sequential order as long as the consumer positively acknowledges them. The message acknowledgment mechanism allows consumers to ask Pulsar to redeliver the message. The consequence is that our processing logic cannot rely on the order of the messages. When the order of messages is critical (publish/unpublish), our algorithm needs to use additional strategies, e.g. event time / sequence id, to properly handle this situation.&lt;/p&gt;
&lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/handsonarchitects/sandbox/blob/main/pulsar/src/test/java/com/handsonarchitects/pulsar/ExclusiveSubscriptionMessageOrderTest.java&quot;&gt;Unit tests&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>