<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hamid Shoja</title>
    <description>The latest articles on DEV Community by Hamid Shoja (@hash01).</description>
    <link>https://dev.to/hash01</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F538427%2Fb88dc832-018a-41a3-ad11-7dc58f0f15b1.jpeg</url>
      <title>DEV Community: Hamid Shoja</title>
      <link>https://dev.to/hash01</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hash01"/>
    <language>en</language>
    <item>
      <title>Coding agents: A silent hook babysits, a loud hook teaches</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Tue, 28 Jul 2026 17:48:00 +0000</pubDate>
      <link>https://dev.to/hash01/ai-skills-a-silent-hook-babysits-a-loud-hook-teaches-1b5h</link>
      <guid>https://dev.to/hash01/ai-skills-a-silent-hook-babysits-a-loud-hook-teaches-1b5h</guid>
      <description>&lt;p&gt;Hi friends&lt;/p&gt;

&lt;p&gt;Third post in a series about setting up coding agents in a real codebase (part 1: &lt;a href="https://dev.to/hash01/how-to-structure-claudemd-skills-and-agents-2p7a"&gt;structuring CLAUDE.md, skills and agents&lt;/a&gt;, part 2: &lt;a href="https://dev.to/hash01/claude-skill-description-o9n"&gt;skill descriptions&lt;/a&gt;). This one is about hooks, and the difference between a hook that fixes and a hook that teaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two flavors of hook
&lt;/h2&gt;

&lt;p&gt;A hook is code that runs on the agent's actions - the surface for rules that must never be skipped. Instructions can be rationalized away ("it's just a filename, close enough"); a hook can't.&lt;/p&gt;

&lt;p&gt;But hooks come in two flavors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;silent-fix&lt;/strong&gt; hook repairs the output automatically. The agent never sees an error, so it keeps generating the wrong pattern forever.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;fail-loudly&lt;/strong&gt; hook blocks and returns an error. The message lands in the agent's context - one failure, one lesson.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Silent is fine when the fix is mechanical and always correct: we run prettier from a hook after every turn, the agent never learns to format, nobody cares. The interesting case is when the fix needs &lt;em&gt;knowledge the hook doesn't have&lt;/em&gt;. Then loud is mandatory.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Postgres migration hook
&lt;/h2&gt;

&lt;p&gt;Our migrations use Flyway, and the file name IS the API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;V012__Billing_AddInvoiceIndex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;sql&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get it wrong and nothing crashes on your machine - Flyway just skips the file or orders it wrong, and you find out in a deploy. It's exactly the kind of rule agents violate: the SQL is perfect, the filename is &lt;code&gt;add_invoice_index.sql&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Could a hook silently rename it? No. A rename needs the next version number, the right schema, a description - knowledge that lives on the agent's side. A silent fix here isn't just pedagogically worse, it's &lt;em&gt;less correct&lt;/em&gt;. So the hook's job is to refuse, and say why:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# PreToolUse hook on Write|Edit: enforce Flyway migration naming.&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'import json,sys; print(json.load(sys.stdin).get("tool_input",{}).get("file_path",""))'&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;/db/migrations/&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0

&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ ^[VB][0-9]&lt;span class="o"&gt;{&lt;/span&gt;3&lt;span class="o"&gt;}&lt;/span&gt;__[A-Za-z][A-Za-z0-9]&lt;span class="k"&gt;*&lt;/span&gt;_[A-Za-z0-9]+&lt;span class="se"&gt;\.&lt;/span&gt;sql&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Blocked: '&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;' is not a valid Flyway migration name."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Expected: V&amp;lt;3-digit-version&amp;gt;__&amp;lt;SchemaName&amp;gt;_&amp;lt;Description&amp;gt;.sql"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Example:  V012__Billing_AddInvoiceIndex.sql"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"(B prefix for baseline migrations. Check existing files for the next version number.)"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;2
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wired up in &lt;code&gt;settings.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write|Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$CLAUDE_PROJECT_DIR/.claude/hooks/migration-name.sh"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens next is the whole point: the write is blocked, the agent reads the rule, checks existing files for the next version, renames to &lt;code&gt;V013__...&lt;/code&gt;, and names every migration correctly for the rest of the session. The violation taught it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, precisely
&lt;/h2&gt;

&lt;p&gt;"Fail loudly" is a choice of three patterns, and the decision fits in a table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Mechanics (Claude Code)&lt;/th&gt;
&lt;th&gt;Use when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Silent fix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;apply fix, &lt;code&gt;exit 0&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;fix is mechanical and always correct (formatting)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Loud fix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;apply fix, print what you fixed to stderr, &lt;code&gt;exit 2&lt;/code&gt; (PostToolUse)&lt;/td&gt;
&lt;td&gt;fix is safe to automate but the pattern should stop appearing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Loud reject&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;no fix, print the rule to stderr, &lt;code&gt;exit 2&lt;/code&gt; (PreToolUse)&lt;/td&gt;
&lt;td&gt;fix needs knowledge the hook doesn't have, or the action is dangerous&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Exit codes, because this is where people get it wrong: &lt;code&gt;exit 0&lt;/code&gt; allows, the agent sees nothing. &lt;code&gt;exit 2&lt;/code&gt; feeds stderr back to the agent - on PreToolUse it also cancels the action; on PostToolUse the action already happened, so it just delivers the lesson. Any other exit code shows stderr to the human only: the agent learns nothing, the worst option for rules.&lt;/p&gt;

&lt;p&gt;Converting a silent autofix into a &lt;strong&gt;loud fix&lt;/strong&gt; is one extra branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Before - agent never learns, hook mops up forever&lt;/span&gt;
eslint &lt;span class="nt"&gt;--fix&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1
&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# After - same fix, and the agent stops making the mistake&lt;/span&gt;
&lt;span class="nv"&gt;before&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git hash-object &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
eslint &lt;span class="nt"&gt;--fix&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1
&lt;span class="nv"&gt;after&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git hash-object &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$before&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$after&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Auto-fixed import order in &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Rule: external imports first, then common/, then relative, sass last."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Write it in that order next time."&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;2
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole solution: detect whether you changed anything, and if you did, say so on stderr and exit 2.&lt;/p&gt;

&lt;p&gt;The migration hook above is the &lt;strong&gt;loud reject&lt;/strong&gt; template: match the path, validate, refuse with the rule plus a valid example. Swap the regex and the message and you've got the same enforcement for branch names, commit formats, whatever your team keeps repeating in review.&lt;/p&gt;

&lt;h2&gt;
  
  
  The error message is a teaching surface
&lt;/h2&gt;

&lt;p&gt;The rejection message is where most hooks fail. It needs four things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What was blocked&lt;/strong&gt; - the exact filename, no ambiguity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The rule&lt;/strong&gt; - the format spec, not just "invalid name"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A valid example&lt;/strong&gt; - agents pattern-match; one example beats three paragraphs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where to look next&lt;/strong&gt; - "check existing files for the next version number"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A hook that just says &lt;code&gt;error: invalid file&lt;/code&gt; blocks but teaches nothing - the agent retries variations and burns tokens guessing. Write the stderr like a review comment from a good colleague.&lt;/p&gt;

&lt;h2&gt;
  
  
  A repeated rejection is a bug report against your docs
&lt;/h2&gt;

&lt;p&gt;Is it OK that this loop repeats every session - violate, reject, correct, forget? No. The lesson dies with the context window, and a rejection that fires session after session means your instruction layer failed: docs and skills are &lt;strong&gt;prevention&lt;/strong&gt; (they load before the action), the hook is the &lt;strong&gt;guarantee&lt;/strong&gt; - and its firings are telemetry on how prevention is doing.&lt;/p&gt;

&lt;p&gt;Fires once in a while: backstop doing its job. Fires repeatedly: a failing test against your docs. Three usual causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The rule isn't in any surface that loads before the action -&amp;gt; add it to the skill that owns the task (our migration rule belongs in the migrations skill, not just the hook).&lt;/li&gt;
&lt;li&gt;The rule exists but the skill never triggers -&amp;gt; a routing bug, fix the skill description (&lt;a href="https://dev.to/hash01/claude-skill-description-o9n"&gt;previous post&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;The rule is in CLAUDE.md but buried in a wall of text -&amp;gt; promote it, shorten it, or move the noise out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One line of bash turns the hook into telemetry - log before the &lt;code&gt;exit 2&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%F&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/.claude/hook-rejections.log"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most-fired rules in that file are exactly the docs most worth fixing. So "hook or fix the skill?" is the wrong question - both, in that order of time: hook immediately, then let the repeat-firings tell you which doc fix pays for itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Loud or silent - the checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fix is mechanical, always correct, and nobody cares if it repeats (formatting)? &lt;strong&gt;Silent fix.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Fix is safe to automate but you're tired of seeing the pattern? &lt;strong&gt;Loud fix.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Fix needs knowledge or judgment (naming, versioning, schema)? &lt;strong&gt;Loud reject.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Violation is expensive or irreversible downstream (prod deploy, data migration)? &lt;strong&gt;Loud reject&lt;/strong&gt;, always.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One sentence to keep: a silent hook babysits, a loud hook teaches. Babysit the whitespace, teach everything else.&lt;/p&gt;

&lt;p&gt;Hope that helped!&lt;br&gt;
Hash&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agentskills</category>
      <category>claude</category>
    </item>
    <item>
      <title>Coding agents: Your skill bodies are fine, your descriptions are broken</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Tue, 28 Jul 2026 17:36:00 +0000</pubDate>
      <link>https://dev.to/hash01/claude-skill-description-o9n</link>
      <guid>https://dev.to/hash01/claude-skill-description-o9n</guid>
      <description>&lt;p&gt;Hi friends&lt;/p&gt;

&lt;p&gt;This is a follow-up to my post about &lt;a href="https://dev.to/hash01/how-to-structure-claudemd-skills-and-agents-2p7a"&gt;structuring CLAUDE.md, skills and agents&lt;/a&gt;. A comment on that post pointed at a failure layer I had completely skipped, and it deserves its own write-up: your skill bodies can be perfect and the agent still never reads them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The invisible bottleneck
&lt;/h2&gt;

&lt;p&gt;Skills load lazily, that's the whole point. Until one triggers, the only thing the model can see is the one-line &lt;code&gt;description&lt;/code&gt; in the frontmatter. Which means routing accuracy is bounded by how well a single sentence discriminates between neighboring skills.&lt;/p&gt;

&lt;p&gt;You can audit every body, verify every code example against the codebase, run retrieval tests on the content... and none of it matters if the model picks the wrong skill, or no skill, or decides the sentence already told it everything it needs.&lt;/p&gt;

&lt;p&gt;Two failure modes to check for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure 1: the description that replaces the skill
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# BAD - the description IS a procedure&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Verifies finished work by running tests, checking the diff&lt;/span&gt;
  &lt;span class="s"&gt;for weakened assertions, and reporting pass/fail&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model reads that sentence, feels it already knows the procedure, and executes a shallow version from memory: run tests, glance at diff, report. It never opens the body, where the actual teeth live ("diff each changed assertion against the base branch; a widened tolerance counts as failure").&lt;/p&gt;

&lt;p&gt;The skill "fired", but the one check that mattered didn't happen. And it looks like success in your logs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# GOOD - trigger only, zero procedure&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use when a refactor or bugfix is complete and you are about&lt;/span&gt;
  &lt;span class="s"&gt;to report it as done. NOT for doc-only changes.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the sentence contains nothing executable, so the model has to read the body to know what to do. The description's only job is routing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure 2: skills that shadow each other
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# skill A&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Testing best practices for this repo&lt;/span&gt;
&lt;span class="c1"&gt;# skill B&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Testing best practices - integration harness, DB fixtures, fake upstreams&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both start with the same phrase. Every testing task matches A first, and the deeper B never loads. Here's the trap: rewriting the bodies changes nothing, the model never gets past the sentence. You can polish skill B forever and it stays invisible.&lt;/p&gt;

&lt;p&gt;Fix it at the description level, with discriminating triggers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# skill A&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use when writing or fixing unit tests for components or pure&lt;/span&gt;
  &lt;span class="s"&gt;functions. NOT for endpoint or database tests.&lt;/span&gt;
&lt;span class="c1"&gt;# skill B&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use when testing HTTP endpoints against a real database or&lt;/span&gt;
  &lt;span class="s"&gt;faked upstream services (integration tests).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test descriptions like you test docs
&lt;/h2&gt;

&lt;p&gt;In the previous post I ran retrieval tests against doc bodies: give a subagent only the docs, no repo access, make it answer implementation questions, grade against the codebase. The extension: run a second version against the &lt;strong&gt;descriptions alone&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Give a subagent just the list of skill descriptions, nothing else, and feed it real task prompts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Write an integration test for the orders endpoint."
"A refactor is done, wrap it up."
"Fix this flaky unit test."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ask one question: which skill would you load, and why? Grade the routing. If the wrong skill wins, or none triggers, or the model says "no need to load anything, the description tells me what to do" - your descriptions failed the test. Fix the sentences, not the bodies.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule of thumb
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The description answers "should I load this skill right now?", never "what will it do once loaded?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you can execute the description, it's carrying content that belongs in the body. A quick checklist for each skill:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;starts with "Use when ..." (a trigger, not a capability)&lt;/li&gt;
&lt;li&gt;says what it's NOT for, if a neighboring skill is close&lt;/li&gt;
&lt;li&gt;contains no procedure verbs (run, check, verify, generate)&lt;/li&gt;
&lt;li&gt;no two skills in the folder could match the same task prompt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One sentence of frontmatter is doing the routing for everything underneath it. Test it like it matters, because it does.&lt;/p&gt;

&lt;p&gt;Hope that helped!&lt;br&gt;
Hash&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>How to structure CLAUDE.md, Skills and Agents</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Sat, 25 Jul 2026 09:00:00 +0000</pubDate>
      <link>https://dev.to/hash01/how-to-structure-claudemd-skills-and-agents-2p7a</link>
      <guid>https://dev.to/hash01/how-to-structure-claudemd-skills-and-agents-2p7a</guid>
      <description>&lt;p&gt;Hi friends&lt;/p&gt;

&lt;p&gt;Here's a tip for when you're setting up Claude Code (or any coding agent) in a real codebase, this came in clutch for me when I discovered our agent docs were actively generating broken code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Most projects end up with four kinds of instruction files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CLAUDE.md&lt;/code&gt; / &lt;code&gt;AGENTS.md&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;skills (&lt;code&gt;.claude/skills/...&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;agent definitions (&lt;code&gt;.claude/agents/...&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;big reference docs (UI library API, internal tooling, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the same knowledge gets copy-pasted into all of them. Each copy drifts separately. When I finally audited every claim in ours against the actual code, it was worse than I thought:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The styling docs showed a CSS-modules pattern that produces class names that &lt;strong&gt;never match&lt;/strong&gt; the build config. Any agent following it ships broken styles.&lt;/li&gt;
&lt;li&gt;The canonical test template set up mocks in &lt;code&gt;beforeAll&lt;/code&gt;, but the test setup file restores all mocks after every test. So the mock silently dies after test one.&lt;/li&gt;
&lt;li&gt;The same template rendered a data-fetching component without its required provider. Instant crash on render.&lt;/li&gt;
&lt;li&gt;The "recommended" test helper was used by exactly 1 of 50 real test files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The docs looked complete. They were confidently wrong. And every wrong doc costs you a multi-thousand-token debug loop when the agent hits it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule of thumb
&lt;/h2&gt;

&lt;p&gt;It's all about &lt;strong&gt;when it loads&lt;/strong&gt; and &lt;strong&gt;who needs it&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Surface&lt;/th&gt;
&lt;th&gt;Loads&lt;/th&gt;
&lt;th&gt;Should own&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CLAUDE.md / AGENTS.md&lt;/td&gt;
&lt;td&gt;always, every session&lt;/td&gt;
&lt;td&gt;rules true for &lt;em&gt;every&lt;/em&gt; edit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skill&lt;/td&gt;
&lt;td&gt;on demand, per task type&lt;/td&gt;
&lt;td&gt;deep how-to knowledge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent&lt;/td&gt;
&lt;td&gt;when delegated&lt;/td&gt;
&lt;td&gt;workflow and gates, not knowledge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hook&lt;/td&gt;
&lt;td&gt;enforced by code&lt;/td&gt;
&lt;td&gt;rules that must never be skipped&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Four questions to route any piece of content:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Applies to every change in the folder?&lt;/strong&gt; Then CLAUDE.md: import style, naming, commands, project structure. Keep it small, it pays token rent every single session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Only needed for one kind of task, but deep?&lt;/strong&gt; Then a skill: styling mechanics, data-fetching patterns, testing recipes. Costs nothing while idle, only the description loads until it triggers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is it a role or a process rather than knowledge?&lt;/strong&gt; Then an agent: the workflow order, the definition of done, which skill to load. Keep agents thin, knowledge buried in an agent file is invisible to everyone else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not acceptable to ever skip?&lt;/strong&gt; Then a hook. Instructions can be rationalized away, code can't. Run your formatter from a hook, not from a sentence asking nicely.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The one rule that stops the rot
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Every fact lives in exactly one file. Everything else links to it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our drift existed precisely because one test template was pasted into three places and evolved independently. After the cleanup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;CLAUDE.md / AGENTS.md  -&amp;gt; rules in 3 lines max + link to the skill
skill                  -&amp;gt; the deep patterns, citing real source files
agent                  -&amp;gt; workflow + "run the check command and observe it pass"
reference doc          -&amp;gt; single home for the heavy API reference
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we can define the boundary in one sentence: if a pattern needs more than a few lines, it goes in the skill and CLAUDE.md gets a link.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify your docs like you verify code
&lt;/h2&gt;

&lt;p&gt;This was the fun part. Docs claims are testable, so test them:&lt;/p&gt;

&lt;p&gt;First, grep before you trust. Every example in your docs should exist in the codebase. Ours didn't:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# "best practice" from our docs vs reality&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rl&lt;/span&gt; &lt;span class="s2"&gt;"prefetchQuery"&lt;/span&gt; src/ | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;      &lt;span class="c"&gt;# 0 uses&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rl&lt;/span&gt; &lt;span class="s2"&gt;"recommendedHelper"&lt;/span&gt; src/ | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;  &lt;span class="c"&gt;# 1 of 50 test files&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run a retrieval test with a subagent. Give it ONLY the doc files, no repo access, and make it answer real implementation questions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Write a test for a component that fetches data."
"Which package does this component come from?"
"Where do global stores live?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grade the answers against the codebase. If the agent following your docs writes code that would fail, your docs failed the test, fix them before merging. We caught every regression this way, before it cost anyone a debug session.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it achieved
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;~11% smaller context per typical feature task, leaner always-on footprint&lt;/li&gt;
&lt;li&gt;a convention change now touches 1 file instead of 3&lt;/li&gt;
&lt;li&gt;and the big one: the docs no longer generate broken styles and failing tests, each of those was a few thousand tokens of debugging every time an agent stepped on it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;this example demonstrates that agent docs are a system with loading semantics, not a wiki: put each fact where its loading model matches, keep one home per fact, and test docs like code.&lt;/p&gt;

&lt;p&gt;Hope that helped!&lt;br&gt;
Hash&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>claude</category>
      <category>ai</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>Why Sequential Reads Beat Random Reads in Postgres at Scale</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Fri, 24 Jul 2026 17:16:00 +0000</pubDate>
      <link>https://dev.to/hash01/why-sequential-reads-beat-random-reads-in-postgres-at-scale-1gjm</link>
      <guid>https://dev.to/hash01/why-sequential-reads-beat-random-reads-in-postgres-at-scale-1gjm</guid>
      <description>&lt;p&gt;We had a large table - hundreds of gigabytes - that needed to be fully exported and processed in batches. The pipeline fetched rows from Postgres in batches of 2000, processed them, and pushed results downstream.&lt;/p&gt;

&lt;p&gt;It was slow. Much slower than expected. The bottleneck wasn't the processing. It was Postgres. Specifically, how we were reading from it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Postgres Stores Data
&lt;/h2&gt;

&lt;p&gt;Before getting to the fix, you need to understand three things: pages, slots, and sequence columns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pages&lt;/strong&gt; are fixed 8KB chunks on disk. Everything Postgres stores lives inside pages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;disk: [page 0][page 1][page 2][page 3]...
       8KB     8KB     8KB     8KB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Slots&lt;/strong&gt; are positions of rows within a page. Each page holds however many rows fit in 8KB - typically 10-20 rows depending on row size.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;page 1 (8KB):
  slot 1: [row data - ~500B]
  slot 2: [row data - ~500B]
  slot 3: [row data - ~500B]
  ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A row's physical address is &lt;code&gt;(page, slot)&lt;/code&gt;. Postgres calls this a &lt;strong&gt;ctid&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A sequence column&lt;/strong&gt; (like an &lt;code&gt;id&lt;/code&gt; or &lt;code&gt;created_at&lt;/code&gt;) is just a business column in your table - a regular value like any other field. It has no relationship to where the row lives on disk.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;page&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;slot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;id:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;892&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;status:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;slot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;id:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;status:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pending"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;slot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;id:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;445&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"baz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;status:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rows are written to the heap in insert order, not id order. Deletes, updates, and bulk loads all shift things around. So id 1, 2, 3 can be on completely different pages scattered across the table.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Index Makes It Worse at Scale
&lt;/h2&gt;

&lt;p&gt;Postgres builds a B-tree index on &lt;code&gt;id&lt;/code&gt; that maps each value to a ctid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Index (B-tree on id):            Heap (actual rows on disk):

id → (page, slot)                page 0: [id=892][id=3  ][id=445]
  1 → (5, 2)                     page 1: [id=7  ][id=1  ][id=22 ]
  2 → (2, 3)                     page 2: [id=2  ][id=567][id=88 ]
  3 → (0, 2)                     ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run &lt;code&gt;WHERE id &amp;gt; X LIMIT 2000&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Postgres walks the B-tree to find matching ids in order&lt;/li&gt;
&lt;li&gt;For each one, jumps to a random heap page to fetch the row&lt;/li&gt;
&lt;li&gt;2000 rows = up to 2000 random page reads, each potentially on a different part of disk&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At small scale this is fine. At hundreds of gigabytes it kills you - the disk head is constantly seeking to new locations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Sequential Scan Fix
&lt;/h2&gt;

&lt;p&gt;A cursor tells Postgres to scan the heap directly, page by page, from start to finish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;DECLARE&lt;/span&gt; &lt;span class="n"&gt;rows_cursor&lt;/span&gt; &lt;span class="k"&gt;CURSOR&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;rows_cursor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- reads ~130 pages sequentially&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;rows_cursor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- continues from where it left off&lt;/span&gt;
&lt;span class="k"&gt;FETCH&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;rows_cursor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- no seeking, no index lookups&lt;/span&gt;
&lt;span class="c1"&gt;-- ...&lt;/span&gt;

&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;FETCH 2000&lt;/code&gt; doesn't mean "read 2000 pages". Each page holds N (e.g. 16) rows, so fetching 2000 rows reads ~130 pages. The cursor remembers where it stopped and the next fetch continues from there.&lt;/p&gt;

&lt;p&gt;Across the entire export, every 8KB page is read exactly once. No page is visited twice. No index lookups. The OS can prefetch pages ahead because the read pattern is completely predictable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sequential vs Random - Side by Side
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Random reads (index scan):
  query 1 → index lookup → jump to page 450 → read 16 rows
  query 2 → index lookup → jump to page 23  → read 16 rows
  query 3 → index lookup → jump to page 891 → read 16 rows
  disk head: seeks constantly

Sequential scan (cursor):
  fetch 1 → page 0  → read 16 rows
  fetch 2 → page 1  → read 16 rows
  fetch 3 → page 2  → read 16 rows
  disk head: moves forward, never back
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same data. Same Postgres. Just read in a straight line instead of jumping around.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Trade-off
&lt;/h2&gt;

&lt;p&gt;The cursor holds an open transaction for the duration of the scan. For a large table that takes hours or days to process, this blocks vacuum, bloats &lt;code&gt;pg_wal&lt;/code&gt;, and holds locks.&lt;/p&gt;

&lt;p&gt;In practice, keyset pagination gets most of the benefit without a long-lived transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- track last seen id, restart cleanly if interrupted&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;last_seen_id&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- update :last_seen_id after each batch and repeat&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't a pure sequential scan but it's close enough - as long as &lt;code&gt;id&lt;/code&gt; correlates with physical insert order, most reads will be forward-moving on disk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;When a full table export is slow, the first thing to check isn't hardware or downstream processing. It's whether you're doing thousands of random index lookups when you could be reading the table in a straight line.&lt;/p&gt;

&lt;p&gt;The index is great for finding a few rows. For reading everything, it's just overhead.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>performance</category>
      <category>etl</category>
    </item>
    <item>
      <title>5- AWS Serverless:: Caching and Event-Driven Design</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Tue, 16 Jun 2026 20:56:00 +0000</pubDate>
      <link>https://dev.to/hash01/aws-caching-and-event-driven-design-2cll</link>
      <guid>https://dev.to/hash01/aws-caching-and-event-driven-design-2cll</guid>
      <description>&lt;p&gt;When building high-scale, resilient systems on AWS, performance isn't just about writing fast code, it’s about avoiding unnecessary work. A Engineer’s philosophy often boils down to a simple truth: &lt;strong&gt;The fastest code is the code that never executes, and the fastest database query is the one you never have to make.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s unpack caching in AWS piece by piece with a few real-world examples.&lt;/p&gt;







&lt;h3&gt;
  
  
  Layer 1: Edge Caching (AWS CloudFront)
&lt;/h3&gt;

&lt;p&gt;AWS CloudFront is a Content Delivery Network (CDN). Instead of forcing every user around the world to request data from your main servers (say, located in Virginia), CloudFront replicates data across hundreds of data centers globally, called &lt;strong&gt;Edge Locations&lt;/strong&gt;. If a user in London requests an asset, CloudFront serves it from a London edge server.&lt;/p&gt;

&lt;p&gt;There are two types of data cached here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Static assets:&lt;/strong&gt; Images, CSS, JavaScript files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cacheable API responses:&lt;/strong&gt; Data from your backend that doesn't change frequently (e.g., a product catalog list).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is controlled using standard web instructions called &lt;strong&gt;&lt;code&gt;Cache-Control&lt;/code&gt; HTTP headers&lt;/strong&gt; sent by your application (e.g., &lt;code&gt;Cache-Control: public, max-age=3600&lt;/code&gt; tells CloudFront to hold onto this data for one hour).&lt;/p&gt;

&lt;h4&gt;
  
  
  Simple Example:
&lt;/h4&gt;

&lt;p&gt;Imagine an e-commerce store. The logo image (&lt;code&gt;logo.png&lt;/code&gt;) and the list of product categories (Shoes, Hats, Shirts) rarely change.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Flow:&lt;/strong&gt; When User A requests the category list, CloudFront fetches it from your database once, saves a copy at the Edge, and delivers it. When Users B through Z request the same list, your database is never touched; CloudFront handles it entirely at the edge.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pipeline Invalidation:&lt;/strong&gt; When the developers deploy a new version of the website code with a new logo, the deployment pipeline automatically runs a command (&lt;code&gt;aws cloudfront create-invalidation&lt;/code&gt;) to forcefully wipe the old logo out of CloudFront's memory globally so users immediately see the new one.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Layer 2: Application Layer Caching (Amazon ElastiCache Redis)
&lt;/h3&gt;

&lt;p&gt;When a request gets past CloudFront and hits your backend compute (AWS Lambda), you don't want the application to immediately hit the primary database if it doesn't have to. we can place &lt;strong&gt;Amazon ElastiCache Redis&lt;/strong&gt; here. Redis is an ultra-fast, in-memory data store.&lt;/p&gt;

&lt;p&gt;There are a few specific choices here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inside the same VPC:&lt;/strong&gt; A Virtual Private Cloud (VPC) is your private, isolated network in AWS. Putting Lambda and Redis in the same VPC ensures they can talk to each other securely and with incredibly low latency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Redis over Memcached:&lt;/strong&gt; Memcached is a simple key-value cache (e.g., store a string, get a string). Redis is much more powerful. It supports &lt;strong&gt;richer data structures&lt;/strong&gt; (like sorted sets, hashes, and lists) and &lt;strong&gt;Pub/Sub (Publish/Subscribe)&lt;/strong&gt;, which allows different parts of your application to broadcast events to each other in real-time.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Simple Example:
&lt;/h4&gt;

&lt;p&gt;Think of a gaming leaderboard or a user's active shopping cart session.&lt;br&gt;
Instead of querying a slow disk-based database every time a player scores a point, you use a Redis &lt;em&gt;Sorted Set&lt;/em&gt;. Redis handles the math and sorting instantly in memory. If you want to notify other services that a score changed, you use Redis &lt;em&gt;Pub/Sub&lt;/em&gt; to broadcast a message: &lt;code&gt;"Player1 just scored!"&lt;/code&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Layer 3: Database Caching (Amazon DAX)
&lt;/h3&gt;

&lt;p&gt;Amazon DynamoDB is a NoSQL database that natively delivers single-digit millisecond latency. For 99% of applications, that is incredibly fast. However, for extreme scale, milliseconds aren't fast enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DAX (DynamoDB Accelerator)&lt;/strong&gt; is a specialized, fully managed write-through cache designed &lt;em&gt;exclusively&lt;/em&gt; for DynamoDB. It sits directly in front of your DynamoDB tables. While DynamoDB responds in milliseconds (1/1000th of a second), DAX responds in &lt;strong&gt;microseconds&lt;/strong&gt; (1/1,000,000th of a second). Because it is a "write-through" cache, when you write data to DAX, it automatically saves it to DynamoDB for you, ensuring the cache is always up to date.&lt;/p&gt;


&lt;h3&gt;
  
  
  Compute Optimization: Lambda Execution Context Reuse
&lt;/h3&gt;

&lt;p&gt;AWS Lambda is serverless. When a request comes in, AWS spins up a tiny container (an &lt;strong&gt;invocation&lt;/strong&gt;), runs your code, and then pauses it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a Lambda container is spun up from scratch, it's called a &lt;strong&gt;Cold Start&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;If a container is reused for a subsequent request, it's called a &lt;strong&gt;Warm Start&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you initialize your database or Redis connection &lt;em&gt;inside&lt;/em&gt; the main function handler, your code will open and close a new connection on &lt;em&gt;every single request&lt;/em&gt;. This destroys performance. By declaring the Redis client outside the handler function (&lt;strong&gt;module scope&lt;/strong&gt;), AWS keeps that connection alive in memory. When a "warm" invocation happens, the Lambda instantly reuses the existing connection.&lt;/p&gt;
&lt;h4&gt;
  
  
  Simple Example (Code Context):
&lt;/h4&gt;

&lt;p&gt;Instead of doing this (Bad):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redisClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;connectToRedis&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Opens a connection EVERY time the function runs&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redisClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do this (Good):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redisClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;connectToRedis&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Module Scope: Runs ONCE during cold start&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Reuses the established connection on all warm starts!&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redisClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Event-Driven Cache Invalidation
&lt;/h3&gt;

&lt;p&gt;Caching has a legendary problem: &lt;em&gt;How do you know when cached data is old (stale) and needs to be deleted?&lt;/em&gt; The engineer uses two strategies to solve this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;TTL-based (Time-to-Live):&lt;/strong&gt; You set an expiration date on data. For example, "Keep this in Redis for 5 minutes, then delete it automatically."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event-Driven Invalidation:&lt;/strong&gt; This is where Event-Driven Design shines. Instead of waiting for a timer to run out, your system reacts to data changes (&lt;strong&gt;mutations&lt;/strong&gt;) in real-time. When a user updates their data, an "event" is triggered. A Lambda function listens to this event and immediately deletes ("invalidates") the old cache key.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Simple Example:
&lt;/h4&gt;

&lt;p&gt;Let's look at a profile update feature:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A user updates their profile bio from "Software Engineer" to "Principal Engineer."&lt;/li&gt;
&lt;li&gt;The change is saved to the primary database (DynamoDB).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Event:&lt;/strong&gt; DynamoDB emits an event via a feature called &lt;em&gt;DynamoDB Streams&lt;/em&gt; saying: &lt;em&gt;"Hey, User 123 just changed their bio!"&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The React:&lt;/strong&gt; An isolated Lambda function wakes up automatically because of that event. It doesn't handle the user's web request; its only job is cache maintenance.&lt;/li&gt;
&lt;li&gt;The Lambda runs a command to delete the old cache key for User 123 in Redis (&lt;code&gt;redis.del("user:123")&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The next time anyone views User 123's profile, the application sees the cache is empty, fetches the brand-new data from the database, and repopulates the cache.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;By combining these layers, we can build a system that is incredibly cost-efficient, lightning-fast, and decoupled&lt;/p&gt;

&lt;p&gt;I hope you find it helpful&lt;br&gt;
Hash&lt;/p&gt;

</description>
      <category>aws</category>
      <category>redis</category>
      <category>elasticcache</category>
      <category>memcache</category>
    </item>
    <item>
      <title>4- AWS Serverless: Event-Driven Design: SQS, SNS, and EventBridge</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Mon, 15 Jun 2026 20:02:00 +0000</pubDate>
      <link>https://dev.to/hash01/aws-event-driven-design-sqs-sns-and-eventbridge-42j</link>
      <guid>https://dev.to/hash01/aws-event-driven-design-sqs-sns-and-eventbridge-42j</guid>
      <description>&lt;p&gt;When designing modern cloud architectures, &lt;strong&gt;Event-Driven Architecture (EDA)&lt;/strong&gt; is the gold standard for creating decoupled, scalable, and resilient systems. However, choosing the right AWS service for passing messages around can be daunting.&lt;/p&gt;

&lt;p&gt;Let’s break down exactly what this means, how each part works, and look at simple real-world examples for each.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: AWS SQS (Simple Queue Service)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;"For resilient pull-based processing with FIFO for ordering guarantees"&lt;/em&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  What it means
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pull-Based Processing:&lt;/strong&gt; Instead of pushing an event directly onto a consumer (which might overwhelm it), messages sit safely in a storage queue. The consumer "pulls" (polls) messages from the queue only when it has the capacity to process them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resilient:&lt;/strong&gt; If your consumer service crashes or experiences a sudden traffic spike, the messages aren’t lost. They wait in the queue until the service recovers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FIFO (First-In, First-Out):&lt;/strong&gt; Standard queues process messages roughly in order, but sometimes duplicate or out-of-order messages happen. A &lt;strong&gt;FIFO queue&lt;/strong&gt; guarantees strict ordering (Message A will &lt;em&gt;always&lt;/em&gt; be processed before Message B if A arrived first) and exactly-once processing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Simple Example: The Coffee Shop
&lt;/h3&gt;

&lt;p&gt;Imagine a busy coffee shop. Customers place orders, and instead of shouting them directly at one overworked barista (push-based), the orders are printed on paper tickets and lined up on a rail (the queue).&lt;/p&gt;

&lt;p&gt;The barista pulls the next ticket from the rail only when they finish the current drink (pull-based processing). If the barista steps away for a moment, the tickets don't vanish; they just wait on the rail (resilient).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why FIFO matters here:&lt;/strong&gt; If Customer A orders a Latte and Customer B orders an Espresso immediately after, the barista must make the Latte first to ensure fairness and prevent Customer A from waiting forever.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part 2: AWS SNS (Simple Notification Service)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;"For fan-out to multiple consumers simultaneously"&lt;/em&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  What it means
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fan-Out Pattern:&lt;/strong&gt; This is a "push-based" publish/subscribe (Pub/Sub) pattern. A publisher sends a message &lt;em&gt;once&lt;/em&gt; to an SNS "Topic," and SNS instantly clones and broadcast-pushes that message to multiple subscribers (consumers) simultaneously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decoupling:&lt;/strong&gt; The service sending the message doesn't know (or care) who is listening. It just drops the message in the bucket, and SNS handles the distribution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Simple Example: The New Author Announcement
&lt;/h3&gt;

&lt;p&gt;Imagine an author finishes a new book. Instead of emailing every single fan individually, they post an update to a subscription newsletter service (the SNS Topic).&lt;/p&gt;

&lt;p&gt;Instantly, that single update is broadcasted ("fanned out") to thousands of subscribers via different channels: some get an email, some get an SMS text, and another system automatically updates the bookstore website inventory.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In a software example:&lt;/strong&gt; When a user buys a product on an e-commerce site, the &lt;code&gt;OrderPlaced&lt;/code&gt; event is sent to SNS. SNS instantly "fans out" this event to three different systems at the exact same time:&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Shipping Service&lt;/strong&gt; queue to pack the item.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Invoicing Service&lt;/strong&gt; queue to charge the card.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Analytics Service&lt;/strong&gt; to update marketing dashboards.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part 3: AWS EventBridge
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;"For complex routing with pattern-based rules... my default for internal domain events"&lt;/em&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  What it means
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complex Routing &amp;amp; Pattern-Based Rules:&lt;/strong&gt; EventBridge is a serverless event bus. Unlike SNS, which blasts messages to everyone subscribed, EventBridge inspects the &lt;em&gt;contents&lt;/em&gt; of the event payload. You can write specific rules like: &lt;em&gt;"Only forward this event if &lt;code&gt;status&lt;/code&gt; is 'FAILED' and &lt;code&gt;location&lt;/code&gt; is 'US'."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal Domain Events:&lt;/strong&gt; A "domain event" is something meaningful that happened in your business logic (e.g., &lt;code&gt;UserUpgradedToPremium&lt;/code&gt;). EventBridge is built to be the central nervous system connecting all your microservices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema Registry &amp;amp; TypeScript Auto-Generation:&lt;/strong&gt; As your team grows, keeping track of what an event looks like (its schema) becomes difficult. EventBridge’s Schema Registry automatically detects the structure of your events. It can then generate code bindings (like TypeScript types). This means developers get auto-complete in their code editors for events happening across the entire company!&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Simple Example: The Airport Sorting System
&lt;/h3&gt;

&lt;p&gt;Think of EventBridge like an automated airport baggage handling system. Every bag (event) has a tag indicating its destination, weight, and airline.&lt;/p&gt;

&lt;p&gt;The main conveyor belt (the Event Bus) scans the tag of every bag and uses rules to route it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the destination is "London," send it to Gate 4.&lt;/li&gt;
&lt;li&gt;If the bag is flagged as "Oversized," route it to the special handling team.&lt;/li&gt;
&lt;li&gt;If it belongs to a third-party partner airline, route it to their terminal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Furthermore, the airport maintains a central database of exactly what a valid baggage tag looks like (Schema Registry), so every airline's computer system knows precisely how to print a compatible tag without manual coordination.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to use which?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use SQS&lt;/strong&gt; when your primary goal is &lt;strong&gt;load-smoothing and safety&lt;/strong&gt;. You want to make sure your database or microservice doesn't break under high traffic, and you need to process items sequentially.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use SNS&lt;/strong&gt; when you want to &lt;strong&gt;broadcast a single event to multiple distinct systems&lt;/strong&gt; instantly, and you want a simple "fire-and-forget" mechanism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use EventBridge&lt;/strong&gt; when you are building an &lt;strong&gt;enterprise microservice mesh&lt;/strong&gt;. It is the ideal choice when you need to route events based on what is &lt;em&gt;inside&lt;/em&gt; them, integrate with SaaS apps (like Zendesk or Stripe), or want strict code-level governance over your event data structures.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;hope you find it helpful&lt;br&gt;
Hash&lt;/p&gt;

</description>
      <category>aws</category>
      <category>sqs</category>
      <category>sns</category>
      <category>eventbridge</category>
    </item>
    <item>
      <title>3- AWS Serverless: REST API vs. HTTP API</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Sun, 14 Jun 2026 18:42:00 +0000</pubDate>
      <link>https://dev.to/hash01/rest-api-vs-http-api-aws-architecture-3pea</link>
      <guid>https://dev.to/hash01/rest-api-vs-http-api-aws-architecture-3pea</guid>
      <description>&lt;p&gt;There is common point of confusion. what's the different between REST API vs. HTTP API in AWS and what's the different between them and a traditional Rest API you write with e.g express in node&lt;/p&gt;

&lt;p&gt;in the broader software world, a "REST API" is just an architectural pattern built on top of HTTP requests.&lt;/p&gt;

&lt;p&gt;The confusion comes entirely from &lt;strong&gt;AWS-specific marketing terminology&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When you are inside the AWS ecosystem, &lt;strong&gt;Amazon API Gateway&lt;/strong&gt; is a specific managed service, and AWS chose to split that service into two different flavors (or software products): one called "REST API" and one called "HTTP API."&lt;/p&gt;

&lt;p&gt;Here is exactly how they work under the hood, how they differ internally, and how it compares to traditional servers.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. How It Works: REST API vs. HTTP API (AWS Architecture)
&lt;/h2&gt;

&lt;p&gt;Think of Amazon API Gateway as a reverse proxy or a "front door" that sits in front of your Lambda functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS REST API (The Heavyweight)
&lt;/h3&gt;

&lt;p&gt;When a request hits an AWS REST API, AWS passes that request through a massive feature pipeline &lt;em&gt;before&lt;/em&gt; it ever touches your Lambda code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Client Request] ──&amp;gt; [Authentication (Cognito/IAM)] ──&amp;gt; [Request Validation] ──&amp;gt; [Data Transformation (VTL)] ──&amp;gt; [Your Lambda]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What happens:&lt;/strong&gt; AWS decrypts the request, validates the JSON schema, checks API keys, runs any custom request transformations using a complex mapping language called VTL, and &lt;em&gt;then&lt;/em&gt; invokes your Lambda function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it costs more:&lt;/strong&gt; You are paying AWS for all that computing power happening inside the API Gateway layer itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AWS HTTP API (The Express Lane)
&lt;/h3&gt;

&lt;p&gt;When a request hits an AWS HTTP API, AWS strips out almost the entire middle pipeline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Client Request] ──&amp;gt; [JWT/OAuth2 Authorization Only] ──&amp;gt; [Your Lambda]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What happens:&lt;/strong&gt; The HTTP API acts as a lightning-fast router. It optionally checks a standard JWT token, converts the incoming HTTP request directly into a clean JSON object, and throws it straight into your Lambda function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why it costs less:&lt;/strong&gt; Because AWS is doing almost zero processing or data manipulation. Your TypeScript Lambda function handles the validation and logic instead.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. The Traditional Service Comparison (Express.js / NestJS)
&lt;/h2&gt;

&lt;p&gt;In a traditional application (like a Node.js/Express app running on a virtual server or Docker container), you don't have this artificial split. You just write a "REST API."&lt;/p&gt;

&lt;p&gt;Here is how the architecture looks side-by-side:&lt;/p&gt;

&lt;h3&gt;
  
  
  Traditional Server (Express.js on EC2/Docker)
&lt;/h3&gt;

&lt;p&gt;In a traditional setup, &lt;strong&gt;one single server&lt;/strong&gt; handles everything.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The server listens for an HTTP request.&lt;/li&gt;
&lt;li&gt;Your Express middleware handles routing (&lt;code&gt;app.get('/hello')&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Your Express middleware handles authorization and data validation.&lt;/li&gt;
&lt;li&gt;Your controller executes the business logic.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  AWS Serverless (HTTP API + Lambda)
&lt;/h3&gt;

&lt;p&gt;In AWS Serverless, the responsibilities are decoupled into completely separate services:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AWS HTTP API&lt;/strong&gt; handles the networking, SSL certificates, and basic routing (&lt;code&gt;GET /hello&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Lambda&lt;/strong&gt; acts as the isolated execution container that runs your TypeScript code to handle the business logic.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Summary: Unmixing the Terms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In the Real World:&lt;/strong&gt; A REST API is a conceptual design pattern. An HTTP request is the underlying protocol. A traditional Node.js backend handles both of these inside the application code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In AWS Language:&lt;/strong&gt; "REST API" and "HTTP API" are just two different &lt;strong&gt;pricing and feature tiers&lt;/strong&gt; of the Amazon API Gateway service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you don't need AWS to do complex request manipulation before your code runs, you choose the &lt;strong&gt;HTTP API&lt;/strong&gt; tier to save 70% on your infrastructure bill and let your TypeScript code do the heavy lifting!&lt;/p&gt;

&lt;p&gt;I hope you find it useful&lt;br&gt;
HASH&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>apigateway</category>
      <category>httpapi</category>
    </item>
    <item>
      <title>2- AWS Serverless: Testing (typescript)</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Fri, 12 Jun 2026 09:31:00 +0000</pubDate>
      <link>https://dev.to/hash01/serverless-typescript-testing-aws-48ab</link>
      <guid>https://dev.to/hash01/serverless-typescript-testing-aws-48ab</guid>
      <description>&lt;p&gt;Shifting from traditional application testing to serverless TypeScript engineering is all about shifting your perspective: &lt;strong&gt;you stop testing a running server, and you start testing how your function responds to events and SDK states.&lt;/strong&gt; Here is a simple, practical example for each testing, using modern AWS SDK v3 syntax.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Unit Testing: Jest + Mock Payloads &amp;amp; SDK Client Mocking
&lt;/h2&gt;

&lt;p&gt;In unit tests, you don't call real AWS services. You pass a simulated API Gateway event to your handler, and you mock the AWS SDK so it returns predictable data instead of hitting live infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code Under Test (&lt;code&gt;src/handler.ts&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;APIGatewayProxyEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;APIGatewayProxyResult&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-lambda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/client-dynamodb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBDocumentClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GetCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/lib-dynamodb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ddbClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DynamoDBClient&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;docClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBDocumentClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ddbClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;APIGatewayProxyEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;APIGatewayProxyResult&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Missing ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Fetch from DynamoDB&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;docClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GetCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;USERS_TABLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Jest Unit Test (&lt;code&gt;tests/unit.test.ts&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Instead of the legacy &lt;code&gt;aws-sdk-mock&lt;/code&gt;, the modern standard for AWS SDK v3 is &lt;code&gt;aws-sdk-client-mock&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../src/handler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;APIGatewayProxyEvent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-lambda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mockClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-sdk-client-mock&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBDocumentClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GetCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/lib-dynamodb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ddbMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mockClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DynamoDBDocumentClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Lambda Handler Unit Test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ddbMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;USERS_TABLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TestTable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should return 200 and user data when user exists&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Mock the AWS SDK Behavior&lt;/span&gt;
    &lt;span class="nx"&gt;ddbMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GetCommand&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;resolves&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Shape the Mocked APIGatewayProxyEvent Payload&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;APIGatewayProxyEvent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Execute handler directly&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockEvent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Integration Testing: LocalStack
&lt;/h2&gt;

&lt;p&gt;Integration tests verify that your code accurately interacts with auxiliary services, without relying on real AWS. LocalStack spins up containerized versions of AWS services inside your CI/CD pipeline or local environment.&lt;/p&gt;

&lt;p&gt;To test against LocalStack, your application code just needs to point its AWS Clients to the local Docker container URL (usually &lt;code&gt;http://localhost:4566&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Integration Test Configuration (&lt;code&gt;tests/integration.test.ts&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CreateTableCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/client-dynamodb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBDocumentClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PutCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/lib-dynamodb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../src/handler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;APIGatewayProxyEvent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-lambda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Point the client to LocalStack instead of real AWS&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;localStackConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:4566&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;accessKeyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;secretAccessKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ddbClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DynamoDBClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;localStackConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;docClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBDocumentClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ddbClient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DynamoDB Integration via LocalStack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Setup: Create the table in LocalStack before tests run&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ddbClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CreateTableCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LocalUsersTable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;AttributeDefinitions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;AttributeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;S&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="na"&gt;KeySchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;KeyType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HASH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="na"&gt;BillingMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PAY_PER_REQUEST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;

    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;USERS_TABLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LocalUsersTable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should successfully read data actually written to LocalStack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Seed real data into the LocalStack DynamoDB container&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;docClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LocalUsersTable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-456&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-456&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;APIGatewayProxyEvent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Run the handler — it will communicate directly with LocalStack&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockEvent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. End-to-End (E2E) Testing: Newman
&lt;/h2&gt;

&lt;p&gt;In E2E testing, your application is fully deployed to a staging environment. You treat it entirely as a black box—sending actual HTTP requests to the live API Gateway URL. Newman allows you to run Postman collections natively via the CLI.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Testing Workflow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;You export a Postman collection containing your test assertions (&lt;code&gt;my-api-tests.json&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;You execute it via your terminal or CI environment against the live URL.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Execute your Postman test collection against your deployed AWS Staging endpoint&lt;/span&gt;
newman run ./tests/my-api-tests.json &lt;span class="nt"&gt;--env-var&lt;/span&gt; &lt;span class="s2"&gt;"baseUrl=https://xyz123.execute-api.us-east-1.amazonaws.com/staging"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Local Execution: AWS SAM Local
&lt;/h2&gt;

&lt;p&gt;Before deploying to staging, you want to see how your Lambda executes inside an isolated Docker container mimicking the real AWS Lambda runtime environment. AWS SAM achieves this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step A: Define your event payload (&lt;code&gt;events/mock-request.json&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pathParameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user-789"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step B: Invoke the function via CLI
&lt;/h3&gt;

&lt;p&gt;Run the SAM build command to compile your TypeScript down to JavaScript, then use &lt;code&gt;sam local invoke&lt;/code&gt; to execute the function with your mock event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Compile TypeScript and build resources&lt;/span&gt;
sam build

&lt;span class="c"&gt;# 2. Locally invoke the function using the mock event&lt;/span&gt;
sam &lt;span class="nb"&gt;local &lt;/span&gt;invoke UserHandlerFunction &lt;span class="nt"&gt;--event&lt;/span&gt; events/mock-request.json

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Hope youe find it helpful&lt;br&gt;
Hash&lt;/p&gt;

</description>
      <category>aws</category>
      <category>testing</category>
      <category>jest</category>
      <category>typescript</category>
    </item>
    <item>
      <title>1- AWS Serverless: Designing a serverless API: Order Processing API (E-commerce)</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Thu, 11 Jun 2026 15:40:00 +0000</pubDate>
      <link>https://dev.to/hash01/designing-a-serverless-api-on-aws-order-processing-api-e-commerce-3ekm</link>
      <guid>https://dev.to/hash01/designing-a-serverless-api-on-aws-order-processing-api-e-commerce-3ekm</guid>
      <description>&lt;p&gt;Modern enterprise order processing architectures must decouple synchronous client demands from asynchronous backend dependencies. Here I'll detail a highly scalable, fault-tolerant design built on AWS. By utilizing an automated API Gateway entry point, specialized Amazon Cognito authentication, optimized AWS Lambda logic blocks, an engineered RDS Proxy connection layer, and an event-driven SQS/EventBridge core, the design guarantees isolation, cost efficiency, and sub-millisecond structural routing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scenario
&lt;/h2&gt;

&lt;p&gt;User places an order → payment is processed → inventory updated → confirmation email sent&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → API Gateway (Cognito auth + validation)
       → Order Lambda (business logic + DynamoDB write)
       → SQS (payment queue)
       → Payment Lambda → EventBridge
                        → Inventory Lambda
                        → Notification Lambda (SES)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy79yqhy6dnrho227u2pl.png" alt="Design" width="800" height="510"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  1- Entry Point — API Gateway
&lt;/h2&gt;

&lt;p&gt;REST endpoint: &lt;code&gt;POST /orders&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Request validation via API Gateway models (reject malformed payloads instantly, no Lambda invoked)&lt;/p&gt;

&lt;p&gt;Auth via Cognito User Pool Authorizer — validates JWT token on every request &lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Request Hits: A client sends a POST /orders request with a JWT token in the header.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Auth Check: API Gateway automatically intercepts the request and validates the JWT against the Cognito User Pool. If expired or spoofed, it returns 410 Gone / 401 Unauthorized right there.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Payload Check: Next, it compares the body against your JSON Schema Model. If a required field like customer_id is missing, API Gateway instantly drops it with a 400 Bad Request.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Win: Your downstream services (like Lambda) are never invoked for bad/unauthorized requests, saving compute costs and protecting against basic DDoS or bad actor spam.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Gotchas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Cognito Latency: While Cognito authorizers are native, they can add a slight latency overhead to your API's P99 metrics during peak traffic. For massive global scale, some enterprises migrate to custom Lambda Authorizers that cache tokens in ElastiCache (Redis).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Model Validation Limits: API Gateway's built-in validator is great for structural checks (e.g., "is this an integer?"), but it cannot do business logic validation (e.g., "is this item SKU actually in our database?"). You still need lightweight validation downstream.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Throttling: Always configure Usage Plans and Rate Limiting at this layer. Without it, a rogue client could overwhelm your backend before your auto-scaling kicks in.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Business Logic — Lambda (TypeScript)
&lt;/h2&gt;

&lt;p&gt;Bundled with esbuild — tiny bundle, fast cold start&lt;/p&gt;

&lt;p&gt;Uses aws-lambda-powertools for structured logging + correlation IDs + tracing&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;p&gt;Bundling (esbuild): Strips unused node modules, removes comments, tree-shakes dead code, and transpiles TypeScript down to a single, lightweight JavaScript file. Less code means the internal Lambda service downloads and instantiates your code container incredibly quickly.&lt;/p&gt;

&lt;p&gt;The Execution Lifecycle: * Initialization (Cold Start): Lambda boots the container and runs code outside the handler function. By not putting heavy SDK packages here, initialization remains ultra-fast.&lt;/p&gt;

&lt;p&gt;Invocation (Warm Start): The handler executes. Because DynamoDBClient was stored in a global variable during the first run, subsequent warm invocations bypass the heavy initialization and dynamic import() statement entirely.&lt;/p&gt;

&lt;p&gt;Powertools Observability: Rather than using console.log, Powertools outputs structured JSON logs. If a customer has an issue, you can trace that specific correlationId seamlessly across your logs, metrics, and X-Ray traces.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gotchas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Memory vs. CPU Trap: Developers often assign Lambda the minimum memory (128MB) to "save money". The catch: AWS scales CPU and network performance proportionally with memory. Upgrading to 1024MB or 1536MB often speeds up cold starts and execution times so drastically that the execution costs remain identical or cheaper while delivering a superior P99 response time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;VPC Cold Starts: If your business logic needs to query an RDS database inside a private VPC, Lambda must attach an Elastic Network Interface (ENI). While AWS optimized this significantly using Hyperplane, it can still add a predictable overhead to cold starts compared to a Lambda running outside a VPC.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Global State Pollution: Global variables (like DynamoDBClient above) persist across warm starts. If you modify a global variable inside a handler (e.g., global error arrays or temporary user arrays), it will bleed into the next customer's request. Always reset request-specific states inside the handler.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conceptual Application Code (TypeScript)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. GLOBAL SCOPE: Warm start re-use (No heavy SDKs imported here)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Logger&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-lambda-powertools/logger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Tracer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-lambda-powertools/tracer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tracer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// 2. LAZY LOADING: Dynamically imported only when needed inside handler&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;DynamoDBClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Clear state/set correlation context&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;correlationId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-Correlation-Id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;awsRequestId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendKeys&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;correlationId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Business Logic Validation (Stock/Price Check)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isStockAvailable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;checkStock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isStockAvailable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;422&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Out of stock&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Lazy load the heavy SDK right before database write&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;DynamoDBClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;DynamoDBClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DB&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/client-dynamodb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;DynamoDBClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Proceed with processing...&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;12345&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Order processing failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Internal Error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Database — RDS
&lt;/h2&gt;

&lt;p&gt;table design — orders, users, inventory &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RDS proxy&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Serverless Connection Problem: Relational databases assign memory to every single open connection. If your API gets a spike in traffic and Lambda scales up to 2,000 concurrent containers, they will try to open 2,000 direct database connections, instantly crashing RDS with an "out of memory" error.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter RDS Proxy: Lambda functions point to the RDS Proxy endpoint instead of the database. The proxy keeps a continuous, optimized pool of connections open to RDS.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Multiplexing: When a Lambda function finishes executing an order (takes 50ms), RDS Proxy immediately claims that connection back and hands it to a different Lambda instance. Your DB only ever sees a stable, flatlined connection count.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Gotchas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Session Pinning: RDS Proxy's primary job is to multiplex connections. However, if your Lambda executes certain commands—like preparing a dynamic SQL statement, changing session variables, or utilizing temporary tables—RDS Proxy gets confused and performs &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;-Session Pinning. This ties that specific Lambda instance to that specific database connection until the Lambda dies, completely destroying the benefits of the proxy pool. Keep queries standard and stateless.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The IAM Secret Storage Lag: RDS Proxy reads the DB password directly from AWS Secrets Manager. If you rotate your database password in an emergency, there can be a tiny window (seconds) of cached credential lag where the proxy might drop connection handshakes.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DynamoDB vs. RDS: If you genuinely want a Partition Key (userId) and Sort Key (orderId), you should drop RDS and switch to Amazon DynamoDB. In an enterprise context, DynamoDB handles high-scale transactional orders infinitely better without needing an RDS Proxy, VPC configurations, or connection pools, though you sacrifice the ability to run complex SQL JOIN statements across your tables.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  High-Level Relational Database Design (DDL)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- 1. Users Table&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="nb"&gt;TIME&lt;/span&gt; &lt;span class="k"&gt;ZONE&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- 2. Orders Table (Composite Index handles the lookups)&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;total_amount&lt;/span&gt; &lt;span class="nb"&gt;DECIMAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="nb"&gt;TIME&lt;/span&gt; &lt;span class="k"&gt;ZONE&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;-- Accelerates "Get all orders for a specific User ordered by date"&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_user_orders&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- 3. Inventory Table&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;item_id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sku&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;stock_quantity&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stock_quantity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="nb"&gt;DECIMAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Async Flow — SQS + EventBridge
&lt;/h2&gt;

&lt;p&gt;After order saved → Lambda drops message to SQS (payment queue)&lt;br&gt;
Payment Lambda picks it up, processes payment&lt;br&gt;
On success → publishes event to EventBridge&lt;br&gt;
EventBridge fans out to:Inventory Lambda (update stock)&lt;br&gt;
Notification Lambda (send email via SES)&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Hand-off:&lt;/strong&gt; Once the Business Logic Lambda from Part 2 saves the order as &lt;code&gt;PENDING&lt;/code&gt;, it drops a lightweight message (e.g., &lt;code&gt;{ "orderId": "abc-123", "amount": 99.00 }&lt;/code&gt;) directly into &lt;strong&gt;SQS&lt;/strong&gt;. The API Gateway can instantly return a &lt;code&gt;202 Accepted&lt;/code&gt; response to the client. The user isn't left waiting on a spinner while the payment processes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Consumer:&lt;/strong&gt; The &lt;strong&gt;Payment Lambda&lt;/strong&gt; continuously polls SQS. It talks to your third-party payment gateway (like Stripe).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Broadcast:&lt;/strong&gt; On successful payment, the Payment Lambda fires a single structured JSON event into &lt;strong&gt;EventBridge&lt;/strong&gt;. It doesn't know—or care—who needs this information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Fan-Out:&lt;/strong&gt; EventBridge evaluates the incoming event pattern. Because it matches a &lt;code&gt;Payment.Success&lt;/code&gt; type, it acts as a traffic cop and duplicates the event, executing both the &lt;strong&gt;Inventory Lambda&lt;/strong&gt; and the &lt;strong&gt;Notification Lambda&lt;/strong&gt; concurrently.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Gotchas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Visibility Timeout Trap:&lt;/strong&gt; Your SQS &lt;code&gt;visibility_timeout_seconds&lt;/code&gt; configuration is hyper-critical. When a Lambda instance reads a message, that message is hidden from other instances for X seconds. If your Payment Lambda hits an API lag with Stripe and takes 31 seconds to complete, but your visibility timeout is set to 30 seconds, SQS will make that message visible &lt;em&gt;again&lt;/em&gt;. A second Lambda will pick it up and process the payment a second time. &lt;strong&gt;Rule of thumb: Visibility timeout must always be &amp;gt; 6 times your Lambda function timeout.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency is Non-Negotiable:&lt;/strong&gt; Because SQS guarantees &lt;em&gt;at-least-once&lt;/em&gt; delivery (network hiccups can cause duplicate messages), your consumers &lt;strong&gt;must&lt;/strong&gt; be idempotent. The Payment Lambda must verify with your DB or payment processor if &lt;code&gt;orderId: abc-123&lt;/code&gt; has already been charged before processing it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EventBridge Latency vs. Throughput:&lt;/strong&gt; EventBridge is built for massive, complex filtering and cross-microservice routing, but it has a slightly higher delivery latency (typically 20–50ms) compared to Amazon SNS (Simple Notification Service). If you require near-instant sub-millisecond fan-out and don't need advanced JSON schema filtering, an &lt;strong&gt;SNS Topic&lt;/strong&gt; might be a faster alternative, though it lacks EventBridge's robust schema registry capabilities.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a enterprise-scale order processing engine on AWS requires balancing system decoupled isolation with a smooth user experience. &lt;/p&gt;

&lt;p&gt;I hope you liked the article and you found it helpful.&lt;/p&gt;

&lt;p&gt;Have questions about this high-level design, or want to discuss alternatives like DynamoDB single-table design? Let me know in the comments below!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>lambda</category>
    </item>
    <item>
      <title>Reclaim Your Mac Disk Space</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Fri, 22 May 2026 11:01:52 +0000</pubDate>
      <link>https://dev.to/hash01/reclaim-your-mac-disk-space-3k49</link>
      <guid>https://dev.to/hash01/reclaim-your-mac-disk-space-3k49</guid>
      <description>&lt;p&gt;Your Mac is nearly full. That &lt;code&gt;98% capacity&lt;/code&gt; warning is real, and it will start causing build failures, crashes, and slowdowns if you ignore it.&lt;/p&gt;

&lt;p&gt;This guide walks through what's eating your disk and exactly how to clean it — fast.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Diagnose First
&lt;/h2&gt;

&lt;p&gt;Always start with a full picture before deleting anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at the &lt;strong&gt;Avail&lt;/strong&gt; column on &lt;code&gt;/dev/diskXXX&lt;/code&gt;. If you see less than &lt;code&gt;1GB&lt;/code&gt;, you're in trouble.&lt;/p&gt;

&lt;p&gt;Note: I also added a &lt;a href="https://github.com/hshoja/ai-skills-collection/tree/main/macos-disk-cleanup" rel="noopener noreferrer"&gt;skill&lt;/a&gt; which you're LLM gives a Full macOS disk audit, it scans all major dirs including hidden dot dirs, outputs prioritized cleanup table with exact commands.&lt;/p&gt;

&lt;p&gt;Then find the biggest offenders in your home folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Caches/&lt;span class="k"&gt;*&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rh&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Application&lt;span class="se"&gt;\ &lt;/span&gt;Support/&lt;span class="k"&gt;*&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rh&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Clear the Caches (Safe, ~N GB+)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;~/Library/Caches&lt;/code&gt; is safe to delete. Every app rebuilds its cache on next launch but you can pick some specific ones too &lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Tame Application Support
&lt;/h2&gt;

&lt;p&gt;This is where the real weight lives. Unlike Caches, &lt;strong&gt;be selective here&lt;/strong&gt; — some folders contain important app data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Application&lt;span class="se"&gt;\ &lt;/span&gt;Support/&lt;span class="k"&gt;*&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rh&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For may Docker Desktop or Rancher Desktop stores its entire Linux VM + all container images.&lt;/p&gt;

&lt;p&gt;If you no longer use Docker just remove it entirly&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Clean Docker Images
&lt;/h2&gt;

&lt;p&gt;Old branch images from months ago are dead weight. Check what you have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker images &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.Repository}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Tag}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Size}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.CreatedSince}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;See how much a full prune would recover:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system &lt;span class="nb"&gt;df&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Remove everything not currently running:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system prune &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Or, conservative — only dangling (&lt;code&gt;&amp;lt;none&amp;gt;&lt;/code&gt;) images:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image prune
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 5: Verify You Recovered Space
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the &lt;strong&gt;Avail&lt;/strong&gt; column again. You should be back to several GBs of free space.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Developer's Mindset on Disk Hygiene
&lt;/h2&gt;

&lt;p&gt;Disk bloat is gradual. You don't notice until you're at &lt;code&gt;100%&lt;/code&gt; and your &lt;code&gt;docker build&lt;/code&gt; silently fails.&lt;/p&gt;

&lt;p&gt;A few habits prevent this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;docker system prune&lt;/code&gt; after finishing a feature branch&lt;/li&gt;
&lt;li&gt;Clear &lt;code&gt;~/Library/Caches&lt;/code&gt; monthly — it's always safe&lt;/li&gt;
&lt;li&gt;Keep an eye on &lt;code&gt;~/Library/Application Support/rancher-desktop&lt;/code&gt; if you use container runtimes&lt;/li&gt;
&lt;li&gt;Old branch images in Docker are never coming back — prune them freely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you found it helpful&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>development</category>
    </item>
    <item>
      <title>A tip from a senior engineer</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Mon, 19 Jan 2026 18:12:06 +0000</pubDate>
      <link>https://dev.to/hash01/a-tip-from-a-senior-engineer-3pe9</link>
      <guid>https://dev.to/hash01/a-tip-from-a-senior-engineer-3pe9</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/hash01" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F538427%2Fb88dc832-018a-41a3-ad11-7dc58f0f15b1.jpeg" alt="hash01"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/hash01/the-trade-off-clean-testing-vs-code-brevity-in-modern-js-12k0" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;The Trade-off: Clean Testing vs. Code Brevity in Modern JS&lt;/h2&gt;
      &lt;h3&gt;Hamid Shoja ・ Jan 19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#frontend&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#backend&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>javascript</category>
      <category>frontend</category>
      <category>node</category>
      <category>backend</category>
    </item>
    <item>
      <title>Uvicorn in Modern Python APIs</title>
      <dc:creator>Hamid Shoja</dc:creator>
      <pubDate>Mon, 19 Jan 2026 18:10:24 +0000</pubDate>
      <link>https://dev.to/hash01/uvicorn-in-modern-python-apis-4m79</link>
      <guid>https://dev.to/hash01/uvicorn-in-modern-python-apis-4m79</guid>
      <description>&lt;p&gt;A quick guide to why Uvicorn is essential for Python developers in 2026, how to use it, and how it stacks up against other languages.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Uvicorn?
&lt;/h2&gt;

&lt;p&gt;Uvicorn is a high-speed ASGI (Asynchronous Server Gateway Interface) server. While frameworks like FastAPI or Starlette help you write your code, Uvicorn is the "engine" that actually runs the code and handles web requests from users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do we use it?
&lt;/h2&gt;

&lt;p&gt;In the past, Python servers (WSGI) could only handle one request at a time per process. Uvicorn uses an asynchronous event loop, allowing it to handle thousands of concurrent connections (like WebSockets or long-polling) without waiting for one to finish before starting the next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Start Example
&lt;/h2&gt;

&lt;p&gt;Install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;uvicorn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Code (main.py):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http.response.start&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;content-type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text/plain&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http.response.body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hello, Dev.to!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvicorn main:app &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Alternatives to Consider
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Hypercorn&lt;/code&gt;: Great if you need HTTP/3 support.&lt;br&gt;
&lt;code&gt;Daphne&lt;/code&gt;: The go-to for Django Channels.&lt;br&gt;
&lt;code&gt;Granian&lt;/code&gt;: A Rust-based runner for those who need even more speed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Architecture Comparison (Python vs. The World)
&lt;/h2&gt;

&lt;p&gt;How does Uvicorn's "Event Loop" compare to other languages in 2026?&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Unicorn&lt;/strong&gt;: ~150k requests/sec; excellent for I/O but limited by the Global Interpreter Lock (GIL).&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Nodejs&lt;/strong&gt;: ~600k requests/sec; highly optimized for I/O-bound web traffic.&lt;br&gt;
&lt;strong&gt;Go (Goroutines)&lt;/strong&gt;: &amp;gt;1M requests/sec; built-in concurrency makes it a "workhorse" for microservices.&lt;br&gt;
&lt;strong&gt;Rust (Tokio/Axum)&lt;/strong&gt;: &amp;gt;1M requests/sec; the fastest model due to lack of garbage collection and zero-cost abstractions.&lt;/p&gt;
&lt;h2&gt;
  
  
  Cpu-intensive tasks
&lt;/h2&gt;

&lt;p&gt;In a single-threaded async loop, one heavy CPU calculation can freeze the entire server. We should offload blocking tasks to a thread pool or a separate worker (like Celery/Arq) to keep the Uvicorn loop responsive.&lt;/p&gt;

&lt;p&gt;Example: Use run_in_executor for blocking code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/heavy-task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;heavy_task&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Don't run blocking code directly; offload it to keep the loop free
&lt;/span&gt;    &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_running_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_in_executor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;some_blocking_heavy_math&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;If you are building a modern Python web app, &lt;code&gt;Uvicorn&lt;/code&gt; is the standard choice. It balances the ease of Python with the speed required for modern, real-time web applications.&lt;/p&gt;

&lt;p&gt;Follow for more bite-sized dev tutorials! 🚀&lt;br&gt;
HASH&lt;/p&gt;

&lt;p&gt;refs:&lt;br&gt;
&lt;a href="https://medium.com/@yogeshkrishnanseeniraj/scaling-django-async-workers-uvicorn-gunicorn-celery-and-redis-full-benchmarking-guide-fef057069e96#:%7E:text=Uvicorn%20alone%20provides%20excellent%20async,with%20low%20to%20moderate%20traffic" rel="noopener noreferrer"&gt;https://medium.com/@yogeshkrishnanseeniraj/scaling-django-async-workers-uvicorn-gunicorn-celery-and-redis-full-benchmarking-guide-fef057069e96#:~:text=Uvicorn%20alone%20provides%20excellent%20async,with%20low%20to%20moderate%20traffic&lt;/a&gt;.&lt;br&gt;
&lt;a href="https://stackoverflow.com/questions/21485920/single-threaded-event-loop-vs-multi-threaded-non-blocking-worker-in-node-js#:%7E:text=As%20a%20minor%20addendum%20I,57.8k10%2093%20131" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/21485920/single-threaded-event-loop-vs-multi-threaded-non-blocking-worker-in-node-js#:~:text=As%20a%20minor%20addendum%20I,57.8k10%2093%20131&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=n0KETvEqiCk&amp;amp;t=57" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=n0KETvEqiCk&amp;amp;t=57&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>python</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
